Data Visualization Tutorial in R

Welcome to the Big Data Analysis and Management Training Program

Welcome to the second section of our comprehensive training program! This section provides a comprehensive guide to data visualization in R using three approaches:

  1. Base R Graphics - Quick exploratory plots
  2. ggplot2 - Publication-quality plots
  3. Plotly - Interactive web-based plots

Each section progresses from simple to complex examples with detailed explanations of function arguments.


Section 1: BASE R GRAPHICS

1.1 Basic Plot Function

# Load built-in dataset
data(mtcars)

# Display dataset information
cat(
  "Dataset: mtcars (Motor Trend Car Road Tests)\n",
  "Rows:", nrow(mtcars), "| Columns:", ncol(mtcars), "\n\n",
  "First 6 rows:\n",
  sep = ""
)
## Dataset: mtcars (Motor Trend Car Road Tests)
## Rows:32| Columns:11
## 
## First 6 rows:
print(head(mtcars))
##                    mpg cyl disp  hp drat    wt  qsec vs am gear carb
## Mazda RX4         21.0   6  160 110 3.90 2.620 16.46  0  1    4    4
## Mazda RX4 Wag     21.0   6  160 110 3.90 2.875 17.02  0  1    4    4
## Datsun 710        22.8   4  108  93 3.85 2.320 18.61  1  1    4    1
## Hornet 4 Drive    21.4   6  258 110 3.08 3.215 19.44  1  0    3    1
## Hornet Sportabout 18.7   8  360 175 3.15 3.440 17.02  0  0    3    2
## Valiant           18.1   6  225 105 2.76 3.460 20.22  1  0    3    1

1.2 Simple Black & White Scatter Plot

# Basic scatter plot with minimal arguments
plot(mtcars$wt,           # x variable: car weight
     mtcars$mpg,          # y variable: miles per gallon
     main = "Weight vs MPG",  # main: plot title
     xlab = "Weight (1000 lbs)",  # xlab: x-axis label
     ylab = "Miles per Gallon",   # ylab: y-axis label
     pch = 16,            # pch: point character (16 = filled circle)
     col = "black",       # col: point color
     cex = 1.0,           # cex: character expansion (point size)
     frame = FALSE)       # frame: remove box around plot

1.3 Adding Color and Customization

# Create color vector based on number of cylinders
colors <- ifelse(mtcars$cyl == 4, "red",
                ifelse(mtcars$cyl == 6, "green", "blue"))

# Enhanced scatter plot with colors and sizes
plot(mtcars$wt, mtcars$mpg,
     main = "Weight vs MPG (Colored by Cylinders)",
     xlab = "Weight (1000 lbs)",
     ylab = "Miles per Gallon",
     pch = ifelse(mtcars$cyl == 4, 16,      # Different shapes for different cylinders
                  ifelse(mtcars$cyl == 6, 17, 18)),
     col = colors,        # Use custom color vector
     cex = ifelse(mtcars$cyl == 4, 1.0,     # Different sizes based on cylinders
                  ifelse(mtcars$cyl == 6, 1.3, 1.6)),
     frame = FALSE,
     lwd = 1.5)           # lwd: line width for point borders

# Add legend
legend("topright",                     # Position: top right corner
       legend = c("4 Cylinders", "6 Cylinders", "8 Cylinders"),  # Text labels
       col = c("red", "green", "blue"), # Colors
       pch = c(16, 17, 18),            # Point characters
       pt.cex = c(1.0, 1.3, 1.6),      # Point sizes
       title = "Cylinders",            # Legend title
       bty = "n")                      # bty: box type ("n" = no box)

1.4 Adding Regression Line and Grid

# Plot with regression line and grid
plot(mtcars$wt, mtcars$mpg,
     main = "Weight vs MPG with Regression Line",
     xlab = "Weight (1000 lbs)",
     ylab = "Miles per Gallon",
     pch = 16,
     col = rgb(0.2, 0.4, 0.8, 0.7),  # RGB colors with transparency (alpha = 0.7)
     cex = 1.2,
     frame = FALSE)

# Add grid lines
grid(col = "gray",        # Grid color
     lty = "dotted",      # lty: line type ("dotted", "dashed", "solid")
     lwd = 0.5)           # lwd: line width

# Add regression line using abline()
abline(lm(mpg ~ wt, data = mtcars),  # Linear model
       col = "red",                   # Line color
       lwd = 2,                       # Line width
       lty = "dashed")                # Line type: dashed

# Add text annotation
text(x = 4.5, y = 30,                # x, y: coordinates for text
     labels = "Negative Correlation", # Text to display
     col = "darkred",                 # Text color
     cex = 1.1)                       # Text size

1.5 Using par() for Multiple Plots

# Save current par settings
old_par <- par()

# Set up 2x2 plot grid
par(mfrow = c(2, 2),        # mfrow: matrix of plots (rows, columns)
    mar = c(4, 4, 3, 1),    # mar: margins (bottom, left, top, right)
    oma = c(2, 2, 2, 0))    # oma: outer margins

# Plot 1: Basic scatter
plot(mtcars$wt, mtcars$mpg,
     main = "Basic Scatter",
     xlab = "Weight",
     ylab = "MPG",
     pch = 16,
     col = "steelblue")

# Plot 2: With regression line
plot(mtcars$wt, mtcars$mpg,
     main = "With Regression",
     xlab = "Weight",
     ylab = "MPG",
     pch = 16,
     col = "forestgreen")
abline(lm(mpg ~ wt, data = mtcars), col = "red", lwd = 2)

# Plot 3: Color by cylinders
colors <- c("red", "green", "blue")[as.factor(mtcars$cyl)]
plot(mtcars$wt, mtcars$mpg,
     main = "By Cylinders",
     xlab = "Weight",
     ylab = "MPG",
     pch = 16,
     col = colors)

# Plot 4: With smooth curve
plot(mtcars$wt, mtcars$mpg,
     main = "With Smooth Curve",
     xlab = "Weight",
     ylab = "MPG",
     pch = 16,
     col = "purple")
lines(lowess(mtcars$wt, mtcars$mpg),  # LOWESS smoother
      col = "orange",
      lwd = 2)

# Add overall title
mtext("Multiple Views of Weight vs MPG",  # Text to display
      side = 3,                           # side: 3 = top
      outer = TRUE,                       # Place in outer margin
      cex = 1.5,                          # Text size
      font = 2)                           # Font: 2 = bold

# Reset to original par settings
par(old_par)

1.6 Histograms with Base R

# Load faithful dataset
data(faithful)

# Display dataset info
cat("Dataset: faithful (Old Faithful Geyser)\n")
## Dataset: faithful (Old Faithful Geyser)
cat("Rows:", nrow(faithful), "\n\n")
## Rows: 272
print(head(faithful))
##   eruptions waiting
## 1     3.600      79
## 2     1.800      54
## 3     3.333      74
## 4     2.283      62
## 5     4.533      85
## 6     2.883      55
# Set up 1x2 plot layout
par(mfrow = c(1, 2))

# Basic histogram
hist(faithful$waiting,          # Data vector
     main = "Basic Histogram",  # Title
     xlab = "Waiting Time (min)", # x-axis label
     ylab = "Frequency",        # y-axis label
     col = "lightblue",         # Fill color
     border = "white",          # Border color
     breaks = 15,               # Number of bins
     freq = TRUE)               # freq: TRUE for frequency, FALSE for density

# Histogram with density curve
hist(faithful$waiting,
     main = "With Density Curve",
     xlab = "Waiting Time (min)",
     ylab = "Density",
     col = rgb(0.8, 0.9, 1, 0.6),  # Light blue with transparency
     border = "navy",
     freq = FALSE,                  # Plot density instead of frequency
     breaks = 20)

# Add density curve
lines(density(faithful$waiting),  # Density estimation
      col = "darkred",            # Line color
      lwd = 2)                    # Line width

# Add rug plot (shows individual data points)
rug(faithful$waiting,             # Data points
    side = 1,                     # side: 1 = bottom
    col = "red",                  # Color
    lwd = 0.5)                    # Line width

# Reset layout
par(mfrow = c(1, 1))

1.7 Box Plots

# Load ToothGrowth dataset
data(ToothGrowth)

# Display dataset info
cat("Dataset: ToothGrowth\n")
## Dataset: ToothGrowth
cat("Rows:", nrow(ToothGrowth), "\n")
## Rows: 60
print(head(ToothGrowth))
##    len supp dose
## 1  4.2   VC  0.5
## 2 11.5   VC  0.5
## 3  7.3   VC  0.5
## 4  5.8   VC  0.5
## 5  6.4   VC  0.5
## 6 10.0   VC  0.5
# Create box plot
boxplot(len ~ supp,            # Formula: length by supplement type
        data = ToothGrowth,    # Data source
        main = "Tooth Growth by Supplement",  # Title
        xlab = "Supplement Type",             # x-axis label
        ylab = "Tooth Length",                # y-axis label
        col = c("lightblue", "lightgreen"),   # Colors for groups
        border = "darkblue",                  # Box border color
        notch = TRUE,                         # notch: add notches for median comparison
        outpch = 16,                          # outpch: outlier point character
        outcol = "red",                       # outcol: outlier color
        outcex = 1.2)                         # outcex: outlier size

# Add points for individual data
stripchart(len ~ supp,          # Formula
           data = ToothGrowth,  # Data
           vertical = TRUE,     # vertical: TRUE for vertical orientation
           method = "jitter",   # method: "jitter" to spread points
           pch = 16,            # Point character
           col = rgb(0, 0, 0, 0.3),  # Semi-transparent black
           cex = 0.8,           # Point size
           add = TRUE)          # add: add to existing plot

1.8 Time Series Plot

# Load AirPassengers dataset
data(AirPassengers)

# Display dataset info
cat("Dataset: AirPassengers (Monthly totals 1949-1960)\n")
## Dataset: AirPassengers (Monthly totals 1949-1960)
print(str(AirPassengers))
##  Time-Series [1:144] from 1949 to 1961: 112 118 132 129 121 135 148 148 136 119 ...
## NULL
print(head(AirPassengers))
##      Jan Feb Mar Apr May Jun
## 1949 112 118 132 129 121 135
# Basic time series plot
plot(AirPassengers,             # Time series object
     main = "Airline Passengers Over Time",  # Title
     xlab = "Year",                         # x-axis label
     ylab = "Passengers (thousands)",       # y-axis label
     type = "l",                # type: "l" = line plot
     col = "blue",              # Line color
     lwd = 2,                   # Line width
     las = 1)                   # las: axis label style (1 = horizontal)

# Add seasonal decomposition lines
decomp <- decompose(AirPassengers)
lines(decomp$trend,             # Trend component
      col = "red",              # Color
      lwd = 2,                  # Line width
      lty = "dashed")           # Line type

# Add legend
legend("topleft",               # Position
       legend = c("Original", "Trend"),  # Labels
       col = c("blue", "red"),  # Colors
       lwd = c(2, 2),           # Line widths
       lty = c("solid", "dashed"),  # Line types
       bty = "n")               # No box around legend

1.9 Base R Exercises

# EXERCISE 1: Create a scatter plot of Sepal.Length vs Sepal.Width from iris dataset
# Requirements:
# 1. Color points by Species
# 2. Add a legend
# 3. Add a title and axis labels
# 4. Add a grid

# EXERCISE 2: Create a histogram of Petal.Length from iris dataset
# Requirements:
# 1. Use different colors for each Species
# 2. Add density curves
# 3. Add appropriate title and labels

# EXERCISE 3: Create a 2x2 plot matrix showing:
# 1. Box plot of mpg by cylinder count
# 2. Histogram of mpg
# 3. Scatter plot of hp vs mpg
# 4. Bar plot of cylinder counts

Section 2: GGPLOT2 VISUALIZATION

print(str(iris))
## 'data.frame':    150 obs. of  5 variables:
##  $ Sepal.Length: num  5.1 4.9 4.7 4.6 5 5.4 4.6 5 4.4 4.9 ...
##  $ Sepal.Width : num  3.5 3 3.2 3.1 3.6 3.9 3.4 3.4 2.9 3.1 ...
##  $ Petal.Length: num  1.4 1.4 1.3 1.5 1.4 1.7 1.4 1.5 1.4 1.5 ...
##  $ Petal.Width : num  0.2 0.2 0.2 0.2 0.2 0.4 0.3 0.2 0.2 0.1 ...
##  $ Species     : Factor w/ 3 levels "setosa","versicolor",..: 1 1 1 1 1 1 1 1 1 1 ...
## NULL
print(head(iris))
##   Sepal.Length Sepal.Width Petal.Length Petal.Width Species
## 1          5.1         3.5          1.4         0.2  setosa
## 2          4.9         3.0          1.4         0.2  setosa
## 3          4.7         3.2          1.3         0.2  setosa
## 4          4.6         3.1          1.5         0.2  setosa
## 5          5.0         3.6          1.4         0.2  setosa
## 6          5.4         3.9          1.7         0.4  setosa

2.1 Introduction to ggplot2

ggplot2 is based on the “Grammar of Graphics” - a systematic approach to building plots layer by layer.

# Basic ggplot2 syntax structure
cat("ggplot2 Basic Syntax:\n",
    "ggplot(data, aes(x, y)) +           # Initialize plot\n",
    "  geom_layer() +                    # Add geometry\n",
    "  scale_*() +                       # Customize scales\n",
    "  theme_*() +                       # Apply theme\n",
    "  labs()                            # Add labels\n",
    sep = "")
## ggplot2 Basic Syntax:
## ggplot(data, aes(x, y)) +           # Initialize plot
##   geom_layer() +                    # Add geometry
##   scale_*() +                       # Customize scales
##   theme_*() +                       # Apply theme
##   labs()                            # Add labels

2.2 Basic ggplot Scatter Plot

# Load iris dataset
data(iris)

# Basic ggplot scatter plot
ggplot(iris,                               # data: dataset
       aes(x = Sepal.Length,               # aes: aesthetic mappings
           y = Sepal.Width,                # x and y variables
           color = Species)) +             # color: map Species to color
  geom_point(size = 3,                     # geom_point: scatter plot layer
             alpha = 0.7) +                # alpha: transparency (0-1)
  labs(title = "Sepal Length vs Width",    # labs: labels and titles
       subtitle = "Iris Dataset",
       x = "Sepal Length (cm)",
       y = "Sepal Width (cm)",
       color = "Species") +                # Legend title
  theme_minimal() +                        # theme: minimal theme
  theme(plot.title = element_text(hjust = 0.5),    # Center title
        plot.subtitle = element_text(hjust = 0.5)) # Center subtitle

2.3 ggplot with Multiple Geometries

# Plot with multiple geometry layers
ggplot(iris, aes(x = Sepal.Length, y = Sepal.Width, color = Species)) +
  geom_point(size = 3,                    # First layer: points
             alpha = 0.6) +
  geom_smooth(method = "lm",              # Second layer: linear regression
              se = TRUE,                  # se: show confidence interval
              formula = y ~ x,            # Formula for smoothing
              alpha = 0.2) +              # Transparency for confidence band
  geom_density_2d(alpha = 0.5,            # Third layer: 2D density contours
                  color = "black") +
  facet_wrap(~ Species,                   # facet_wrap: separate plots by Species
             ncol = 3) +                  # ncol: number of columns
  labs(title = "Sepal Dimensions with Regression",
       x = "Sepal Length (cm)",
       y = "Sepal Width (cm)") +
  theme_bw() +                            # Black and white theme
  theme(legend.position = "none")         # Hide legend (redundant with facets)

2.4 Histograms and Density Plots with ggplot

# Create comparison plot
p1 <- ggplot(faithful, aes(x = waiting)) +
  geom_histogram(binwidth = 5,            # binwidth: width of bins
                 fill = "lightblue",      # fill: interior color
                 color = "black",         # color: border color
                 alpha = 0.7) +           # alpha: transparency
  labs(title = "Histogram", 
       x = "Waiting Time (min)", 
       y = "Count") +
  theme_minimal()

p2 <- ggplot(faithful, aes(x = waiting)) +
  geom_density(fill = "lightgreen",       # Density plot fill
               alpha = 0.5, 
               color = "darkgreen") +
  labs(title = "Density Plot",
       x = "Waiting Time (min)", 
       y = "Density") +
  theme_minimal()

p3 <- ggplot(faithful, aes(x = waiting)) +
  geom_histogram(aes(y = ..density..),    # ..density..: use density instead of count
                 binwidth = 5,
                 fill = "lightcoral",
                 alpha = 0.5) +
  geom_density(color = "darkred", 
               size = 1) +                # size: line thickness
  labs(title = "Histogram + Density",
       x = "Waiting Time (min)", 
       y = "Density") +
  theme_minimal()

# Arrange plots using patchwork (install if needed: install.packages("patchwork"))
if(require(patchwork)) {
  p1 + p2 + p3 + plot_layout(ncol = 3)
} else {
  print(p1)
  print(p2)
  print(p3)
}

2.5 Box Plots and Violin Plots

# Create ToothGrowth plot
ggplot(ToothGrowth, 
       aes(x = factor(dose),          # factor(): treat dose as categorical
           y = len, 
           fill = factor(dose))) +    # fill: map dose to fill color
  geom_violin(alpha = 0.6,            # Violin plot
              trim = FALSE) +         # trim: don't trim tails
  geom_boxplot(width = 0.2,           # Box plot inside violin
               alpha = 0.8) +    
  geom_jitter(width = 0.1,            # Jittered points
              size = 1.5, 
              alpha = 0.5) +
  labs(title = "Tooth Growth by Dose",
       subtitle = "Violin + Box + Jitter Plot",
       x = "Dose (mg/day)",
       y = "Tooth Length",
       fill = "Dose") +
  scale_fill_brewer(palette = "Set2") +  # ColorBrewer palette
  theme_classic()                      # Classic theme

2.6 Bar Plots

# Prepare Titanic data
titanic_df <- as.data.frame(Titanic)
print(head(titanic_df))
##   Class    Sex   Age Survived Freq
## 1   1st   Male Child       No    0
## 2   2nd   Male Child       No    0
## 3   3rd   Male Child       No   35
## 4  Crew   Male Child       No    0
## 5   1st Female Child       No    0
## 6   2nd Female Child       No    0
# Bar plot
ggplot(titanic_df, 
       aes(x = Class,                  # x: categorical variable
           y = Freq,                   # y: frequency
           fill = Survived)) +         # fill: color by survival
  geom_bar(stat = "identity",          # stat: use actual y values
           position = "dodge",         # position: bars side by side
           width = 0.7) +              # width: bar width (0-1)
  
  labs(title = "Titanic Survival by Class",
       x = "Passenger Class",
       y = "Count",
       fill = "Survived") +
  scale_fill_manual(values = c("Yes" = "#4daf4a", "No" = "#e41a1c")) +  # Custom colors
  theme_minimal() +
  theme(axis.text.x = element_text(angle = 45, hjust = 1))  # Rotate x labels

2.7 Heatmaps

# Prepare volcano data
print(head(volcano))
##      [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10] [,11] [,12] [,13] [,14]
## [1,]  100  100  101  101  101  101  101  100  100   100   101   101   102   102
## [2,]  101  101  102  102  102  102  102  101  101   101   102   102   103   103
## [3,]  102  102  103  103  103  103  103  102  102   102   103   103   104   104
## [4,]  103  103  104  104  104  104  104  103  103   103   103   104   104   104
## [5,]  104  104  105  105  105  105  105  104  104   103   104   104   105   105
## [6,]  105  105  105  106  106  106  106  105  105   104   104   105   105   106
##      [,15] [,16] [,17] [,18] [,19] [,20] [,21] [,22] [,23] [,24] [,25] [,26]
## [1,]   102   102   103   104   103   102   101   101   102   103   104   104
## [2,]   103   103   104   105   104   103   102   102   103   105   106   106
## [3,]   104   104   105   106   105   104   104   105   106   107   108   110
## [4,]   105   105   106   107   106   106   106   107   108   110   111   114
## [5,]   105   106   107   108   108   108   109   110   112   114   115   118
## [6,]   106   107   109   110   110   112   113   115   116   118   119   121
##      [,27] [,28] [,29] [,30] [,31] [,32] [,33] [,34] [,35] [,36] [,37] [,38]
## [1,]   105   107   107   107   108   108   110   110   110   110   110   110
## [2,]   107   109   110   110   110   110   111   112   113   114   116   115
## [3,]   111   113   114   115   114   115   116   118   119   119   121   121
## [4,]   117   118   117   119   120   121   122   124   125   126   127   127
## [5,]   121   122   121   123   128   131   129   130   131   131   132   132
## [6,]   124   126   126   129   134   137   137   136   136   135   136   136
##      [,39] [,40] [,41] [,42] [,43] [,44] [,45] [,46] [,47] [,48] [,49] [,50]
## [1,]   110   110   108   108   108   107   107   108   108   108   108   108
## [2,]   114   112   110   110   110   109   108   109   109   109   109   108
## [3,]   120   118   116   114   112   111   110   110   110   110   109   109
## [4,]   126   124   122   120   117   116   113   111   110   110   110   109
## [5,]   131   130   128   126   122   119   115   114   112   110   110   110
## [6,]   136   135   133   129   126   122   118   116   115   113   111   110
##      [,51] [,52] [,53] [,54] [,55] [,56] [,57] [,58] [,59] [,60] [,61]
## [1,]   107   107   107   107   106   106   105   105   104   104   103
## [2,]   108   108   108   107   107   106   106   105   105   104   104
## [3,]   109   109   108   108   107   107   106   106   105   105   104
## [4,]   109   109   109   108   108   107   107   106   106   105   105
## [5,]   110   110   109   109   108   107   107   107   106   106   105
## [6,]   110   110   110   109   108   108   108   107   107   106   106
volcano_long <- reshape2::melt(volcano)
print(head(volcano_long))
##   Var1 Var2 value
## 1    1    1   100
## 2    2    1   101
## 3    3    1   102
## 4    4    1   103
## 5    5    1   104
## 6    6    1   105
# Colors in R
par(mfrow=c(1,5)); z <- matrix(volcano, nrow(volcano)); 
lapply(list(terrain.colors, 
            topo.colors, 
            heat.colors, 
            cm.colors, 
            rainbow), \(f) image(z, col=f(100), axes=FALSE))

# Heatmap with ggplot
ggplot(volcano_long, 
       aes(x = Var1,                   # x coordinate
           y = Var2,                   # y coordinate
           fill = value)) +            # fill: color by value
  geom_tile() +                        # geom_tile: creates heatmap
  scale_fill_gradientn(colors = terrain.colors(10),  # Color gradient
                       name = "Height") +            # Legend title
  labs(title = "Volcano Topography Heatmap",
       x = "X Coordinate",
       y = "Y Coordinate") +
  theme_minimal() +
  theme(panel.grid = element_blank())  # Remove grid lines

2.8 Custom Themes and Saving Plots

# Create custom plot
custom_plot <- ggplot(iris, aes(x = Species, y = Sepal.Length, fill = Species)) +
  geom_boxplot(alpha = 0.8,
               outlier.color = "red",
               outlier.size = 2) +
  geom_jitter(width = 0.2, 
              size = 1.5, 
              alpha = 0.4) +
  labs(title = "Sepal Length by Species",
       subtitle = "Iris Dataset Analysis",
       x = "Species",
       y = "Sepal Length (cm)",
       caption = "Source: Fisher's Iris Dataset") +
  scale_fill_brewer(palette = "Set3") +
  
  # Custom theme
  theme(
    plot.title = element_text(size = 16,           # Title size
                              face = "bold",       # Font face
                              hjust = 0.5),       # Horizontal justification
    plot.subtitle = element_text(size = 12,
                                 hjust = 0.5),
    axis.title = element_text(size = 12,           # Axis title size
                              face = "bold"),
    axis.text = element_text(size = 10),          # Axis text size
    legend.title = element_text(face = "bold"),
    legend.position = "bottom",                   # Legend position
    panel.background = element_rect(fill = "white"),  # Panel background
    panel.grid.major = element_line(color = "gray90",  # Major grid lines
                                    size = 0.5),
    panel.grid.minor = element_blank(),           # Remove minor grid
    plot.background = element_rect(fill = "white",  # Plot background
                                   color = "black",
                                   size = 1)
  )

print(custom_plot)

# Save plot
ggsave("figures/custom_ggplot.png",        # filename
       plot = custom_plot,                 # plot to save
       width = 10,                         # width in inches
       height = 6,                         # height in inches
       dpi = 300)                          # resolution

2.9 ggplot2 Exercises

# EXERCISE 4: Create a ggplot of mtcars showing:
# 1. Scatter plot of mpg vs hp
# 2. Color points by transmission type (am)
# 3. Add smooth trend line
# 4. Facet by number of cylinders
# 5. Apply theme_classic()

# EXERCISE 5: Create a bar plot of diamond counts by cut
# 1. Use diamonds dataset (ggplot2::diamonds)
# 2. Fill bars by color
# 3. Use position = "fill" for proportions
# 4. Add percentage labels

# EXERCISE 6: Create a line plot of economics dataset
# 1. Plot unemployment rate over time
# 2. Add vertical line for significant events
# 3. Add shaded region for recession periods
# 4. Use scale_x_date() for proper date formatting

Section 3: PLOTLY INTERACTIVE VISUALIZATION

3.1 Introduction to Plotly

# Basic plotly syntax
cat(
  "plotly Basic Syntax:\n",
  "plot_ly(data, x = ~var1, y = ~var2, type = 'scatter', mode = 'markers')\n",
  "\nCommon type values:\n",
  "- 'scatter': scatter/line plots\n",
  "- 'bar': bar charts\n",
  "- 'histogram': histograms\n",
  "- 'box': box plots\n",
  "- 'heatmap': heatmaps\n",
  sep = ""
)
## plotly Basic Syntax:
## plot_ly(data, x = ~var1, y = ~var2, type = 'scatter', mode = 'markers')
## 
## Common type values:
## - 'scatter': scatter/line plots
## - 'bar': bar charts
## - 'histogram': histograms
## - 'box': box plots
## - 'heatmap': heatmaps

3.2 Basic Interactive Scatter Plot

# Basic interactive scatter plot
p <- plot_ly(mtcars,                      # data: dataset
             x = ~wt,                     # x: weight (~ means formula)
             y = ~mpg,                    # y: mpg
             type = 'scatter',            # type: plot type
             mode = 'markers',            # mode: display mode
             marker = list(size = 10,     # marker: point properties
                           color = 'rgba(30, 120, 180, 0.8)',  # RGBA color
                           line = list(color = 'rgb(0,0,0)',   # Border color
                                       width = 1)),            # Border width
             text = ~paste('Car:', rownames(mtcars),  # text: hover text
                           '<br>MPG:', mpg,
                           '<br>Weight:', wt),
             hoverinfo = 'text') %>%      # hoverinfo: what to show on hover
  layout(title = 'Interactive Scatter Plot',  # layout: plot layout
         xaxis = list(title = 'Weight (1000 lbs)'),  # xaxis properties
         yaxis = list(title = 'Miles per Gallon'),   # yaxis properties
         hovermode = 'closest')           # hovermode: how hover works

p

3.3 Colored Scatter with Legend

# Colored scatter plot with groups
p <- plot_ly(mtcars,
             x = ~wt,
             y = ~mpg,
             color = ~factor(cyl),        # color: map to color scale
             colors = c('#e41a1c', '#377eb8', '#4daf4a'),  # Custom colors
             type = 'scatter',
             mode = 'markers',
             size = ~hp,                  # size: map to point size
             sizes = c(5, 20),            # sizes: min and max size
             marker = list(opacity = 0.7, # opacity: transparency
                           sizemode = 'diameter'),  # sizemode: how to interpret size
             text = ~paste('Car:', rownames(mtcars),
                           '<br>Cylinders:', cyl,
                           '<br>HP:', hp),
             hoverinfo = 'text') %>%
  layout(title = 'Weight vs MPG (Interactive)',
         xaxis = list(title = 'Weight'),
         yaxis = list(title = 'MPG'),
         legend = list(title = list(text = 'Cylinders')),  # Legend title
         hovermode = 'closest')

p

3.4 3D Scatter Plot

# Interactive 3D scatter plot
p <- plot_ly(mtcars,
             x = ~wt,
             y = ~mpg,
             z = ~hp,                     # z: third dimension
             color = ~factor(cyl),
             colors = c('#ff7f00', '#984ea3', '#ffff33'),
             type = 'scatter3d',          # type: 3D scatter
             mode = 'markers',
             marker = list(size = 5,
                           opacity = 0.8),
             text = ~rownames(mtcars)) %>%
  layout(title = '3D Scatter Plot',
         scene = list(                    # scene: 3D scene properties
           xaxis = list(title = 'Weight'),
           yaxis = list(title = 'MPG'),
           zaxis = list(title = 'Horsepower'),
           camera = list(                 # camera: viewing angle
             eye = list(x = 1.5, y = 1.5, z = 1.5)  # eye position
           )
         ))

p

3.5 Interactive Time Series

# Create time series data
dates <- seq.Date(from = as.Date('2020-01-01'), 
                  by = 'month', 
                  length.out = 24)
sales <- cumsum(rnorm(24, mean = 100, sd = 20))

# Interactive line plot
p <- plot_ly(x = dates,                   # x: dates
             y = sales,                    # y: sales
             type = 'scatter',
             mode = 'lines+markers',      # mode: lines and markers
             line = list(color = 'rgb(31, 119, 180)',  # line properties
                         width = 2,
                         dash = 'solid'),
             marker = list(size = 8,
                           color = 'rgb(255, 127, 14)'),
             name = 'Sales') %>%          # name: trace name
  layout(title = 'Sales Over Time',
         xaxis = list(title = 'Date',
                      type = 'date',      # type: date axis
                      tickformat = '%b %Y'),  # Date format
         yaxis = list(title = 'Sales ($)'),
         hovermode = 'x unified')         # Show all y values at x position

p

3.6 Interactive Bar Plot

# Prepare Titanic data for plotly
titanic_summary <- aggregate(Freq ~ Class + Survived, 
                            data = titanic_df, 
                            sum)

# Interactive bar plot
p <- plot_ly(titanic_summary,
             x = ~Class,
             y = ~Freq,
             color = ~Survived,
             colors = c('#d7191c', '#2c7bb6'),
             type = 'bar',
             text = ~Freq,
             textposition = 'auto',       # textposition: auto position text
             hovertext = ~paste('Class:', Class,  # hovertext: custom hover
                               '<br>Survived:', Survived,
                               '<br>Count:', Freq),
             hoverinfo = 'text') %>%
  layout(title = 'Titanic Survival by Class',
         xaxis = list(title = 'Passenger Class'),
         yaxis = list(title = 'Count'),
         barmode = 'group',               # barmode: grouped bars
         bargap = 0.15,                   # bargap: gap between bars
         bargroupgap = 0.1,               # bargroupgap: gap between groups
         hoverlabel = list(namelength = -1))  # Show full hover label

p

3.7 Interactive Histogram

# Interactive histogram
p <- plot_ly(x = faithful$waiting,
             type = 'histogram',
             nbinsx = 20,                 # nbinsx: number of bins
             marker = list(color = 'rgb(158,202,225)',  # Bar color
                           line = list(color = 'rgb(8,48,107)',  # Border color
                                       width = 1.5)),
             opacity = 0.7,               # opacity: transparency
             name = 'Waiting Time') %>%   # name: legend name
  layout(title = 'Waiting Time Distribution',
         xaxis = list(title = 'Waiting Time (minutes)',
                      range = c(40, 100)),  # range: axis range
         yaxis = list(title = 'Count'),
         bargap = 0.05,                   # bargap: gap between bars
         hovermode = 'x')                 # Show histogram bin info

p

3.8 Plotly Exercises

# EXERCISE 7: Create an interactive plot of quakes dataset
# 1. 2D scatter of lat vs long
# 2. Color by depth
# 3. Size by magnitude
# 4. Add hover information with all variables

# EXERCISE 8: Create interactive volcano surface plot
# 1. Use plot_ly type = "surface"
# 2. Add contours
# 3. Customize colorscale
# 4. Add lighting effects

# EXERCISE 9: Create dashboard with subplots
# 1. Combine scatter, histogram, and box plot
# 2. Link selections between plots
# 3. Add dropdown menus for variable selection

Section 4: BEST PRACTICES AND COMPARISON

4.1 When to Use Each Tool

# Create comparison data frame
comparison <- data.frame(
  Feature = c("Learning Curve", "Customization", "Interactivity", 
              "Publication Quality", "Speed", "Ease of Use"),
  Base_R = c("Easy", "Basic", "None", "Basic", "Fast", "Very Easy"),
  ggplot2 = c("Moderate", "Excellent", "Limited", "Excellent", "Moderate", "Moderate"),
  Plotly = c("Steep", "Good", "Excellent", "Good", "Slow", "Complex")
)

# Display as interactive table
DT::datatable(comparison,
              options = list(pageLength = 6, 
                             dom = 't'),
              rownames = FALSE) %>%
  DT::formatStyle(columns = 1:4, 
                  fontSize = '12px')

4.2 Color Palette Best Practices

# Show different color palettes
par(mfrow = c(1, 3), mar = c(2, 2, 2, 1))

# Sequential palette (for ordered data)
image(volcano[1:10, 1:10], 
      col = brewer.pal(9, "Blues"),
      main = "Sequential (Blues)")

# Diverging palette (for data with midpoint)
image(volcano[1:10, 1:10] - mean(volcano[1:10, 1:10]), 
      col = brewer.pal(11, "RdBu"),
      main = "Diverging (RdBu)")

# Qualitative palette (for categorical data)
barplot(rep(1, 8), 
        col = brewer.pal(8, "Set3"),
        main = "Qualitative (Set3)",
        border = NA)

par(mfrow = c(1, 1))

4.3 Exporting Plots

# Export Base R plot
png("figures/base_scatter.png",    # filename
    width = 2000,                  # width in pixels
    height = 1500,                 # height in pixels
    res = 300)                     # resolution (DPI)
plot(mtcars$wt, mtcars$mpg,
     main = "Exported Plot",
     xlab = "Weight",
     ylab = "MPG")
dev.off()

# Export ggplot
ggsave("figures/ggplot_export.png",
       plot = custom_plot,
       width = 10,     # inches
       height = 6,     # inches
       dpi = 300)

# Export plotly (as HTML)
htmlwidgets::saveWidget(p, "figures/plotly_export.html")

4.4 Performance Tips

cat("Performance Optimization Tips:\n\n",
    "• For large datasets (>10k points):\n",
    "  - Use hexbin plots or 2D density\n",
    "  - Sample data for initial exploration\n",
    "  - Consider data aggregation\n\n",
    "• Memory management:\n",
    "  - Remove unused objects: rm(object)\n",
    "  - Clear plots: dev.off()\n",
    "  - Run garbage collection: gc()\n\n",
    "• Plotting speed:\n",
    "  - Base R: fastest for simple plots\n",
    "  - ggplot2: slower, more features\n",
    "  - Plotly: slowest, but interactive\n"
)
## Performance Optimization Tips:
## 
##  • For large datasets (>10k points):
##    - Use hexbin plots or 2D density
##    - Sample data for initial exploration
##    - Consider data aggregation
## 
##  • Memory management:
##    - Remove unused objects: rm(object)
##    - Clear plots: dev.off()
##    - Run garbage collection: gc()
## 
##  • Plotting speed:
##    - Base R: fastest for simple plots
##    - ggplot2: slower, more features
##    - Plotly: slowest, but interactive

FINAL PROJECT

# FINAL PROJECT: Create a Visualization Dashboard
# 
# Choose any dataset (suggestions: gapminder, diamonds, economics)
# 
# Requirements:
# 1. Create at least 3 different plot types
# 2. Use all three plotting systems (Base R, ggplot2, Plotly)
# 3. Include:
#    - Proper labels and titles
#    - Legends where appropriate
#    - Color schemes
#    - Theme customization
# 
# 4. Export all plots
# 5. Write brief analysis of findings
# 
# Example workflow:
# 1. Load and explore data
# 2. Create Base R plots for quick exploration
# 3. Create ggplot2 plots for publication
# 4. Create Plotly plots for interactivity
# 5. Compare insights from different visualizations

SUMMARY

Key Takeaways

  1. Base R is best for quick exploratory analysis and simple plots
  2. ggplot2 excels at creating publication-quality, complex visualizations
  3. Plotly is ideal for interactive, web-based dashboards
  4. Always consider your audience and purpose when choosing a plotting method
  5. Good visualizations tell a story - use titles, labels, and annotations effectively

Additional Resources


This material is part of the training program by The National Centre for Research Methods © NCRM authored by Dr Somnath Chaudhuri (University of Southampton). Content is under a CC BY‑style permissive license and can be freely used for educational purposes with proper attribution.

LS0tDQp0aXRsZTogIkRhdGEgVmlzdWFsaXphdGlvbiBVc2luZyBSOiBCYXNlIFIsIGdncGxvdDIgJiBQbG90bHkiDQphdXRob3I6ICJTb21uYXRoIENoYXVkaHVyaSwgVW5pdmVyc2l0eSBvZiBTb3V0aGFtcHRvbiwgVUsiDQpkYXRlOiAiYHIgZm9ybWF0KFN5cy5EYXRlKCksICclQiAlZCwgJVknKWAiDQpvdXRwdXQ6DQogIGh0bWxfZG9jdW1lbnQ6DQogICAgdG9jOiB0cnVlDQogICAgdG9jX2RlcHRoOiAzDQogICAgdG9jX2Zsb2F0OiB0cnVlDQogICAgdGhlbWU6IGNvc21vDQogICAgaGlnaGxpZ2h0OiB0YW5nbw0KICAgIGNvZGVfZm9sZGluZzogc2hvdw0KICAgIGNvZGVfZG93bmxvYWQ6IHRydWUNCiAgcGRmX2RvY3VtZW50Og0KICAgIHRvYzogdHJ1ZQ0KICAgIHRvY19kZXB0aDogMw0KZ2VvbWV0cnk6IG1hcmdpbj0xaW4NCmZvbnRzaXplOiAxMXB0DQotLS0NCg0KYGBge3Igc2V0dXAsIGluY2x1ZGU9RkFMU0V9DQojIFNldHVwIGNodW5rIC0gcnVucyBvbmNlIGF0IHRoZSBiZWdpbm5pbmcNCiMgVGhpcyBjb25maWd1cmVzIGtuaXRyIG9wdGlvbnMgYW5kIGxvYWRzIHJlcXVpcmVkIHBhY2thZ2VzDQprbml0cjo6b3B0c19jaHVuayRzZXQoDQogIGVjaG8gPSBUUlVFLCAgICAgICAgICAjIFNob3cgY29kZSBpbiBvdXRwdXQNCiAgd2FybmluZyA9IEZBTFNFLCAgICAgICMgSGlkZSB3YXJuaW5nIG1lc3NhZ2VzDQogIG1lc3NhZ2UgPSBGQUxTRSwgICAgICAjIEhpZGUgcGFja2FnZSBsb2FkaW5nIG1lc3NhZ2VzDQogIGZpZy53aWR0aCA9IDEwLCAgICAgICAjIEZpZ3VyZSB3aWR0aCBpbiBpbmNoZXMNCiAgZmlnLmhlaWdodCA9IDYsICAgICAgICMgRmlndXJlIGhlaWdodCBpbiBpbmNoZXMNCiAgZmlnLmFsaWduID0gJ2NlbnRlcicsICMgQ2VudGVyIGFsaWduIGZpZ3VyZXMNCiAgb3V0LndpZHRoID0gIjkwJSIsICAgICMgT3V0cHV0IHdpZHRoICg5MCUgb2YgY29udGFpbmVyKQ0KICBjYWNoZSA9IEZBTFNFICAgICAgICAgIyBEaXNhYmxlIGNhY2hpbmcgZm9yIHJlbGlhYmlsaXR5DQopDQoNCiMgTG9hZCByZXF1aXJlZCBwYWNrYWdlcw0KIyBJbnN0YWxsIHBhY2thZ2VzIGZpcnN0IGlmIG5lZWRlZDogaW5zdGFsbC5wYWNrYWdlcyhjKCJnZ3Bsb3QyIiwgInBsb3RseSIsICJEVCIsICJSQ29sb3JCcmV3ZXIiKSkNCmxpYnJhcnkoZ2dwbG90MikgICAgICAgICMgQWR2YW5jZWQgcGxvdHRpbmcgc3lzdGVtDQpsaWJyYXJ5KHBsb3RseSkgICAgICAgICAjIEludGVyYWN0aXZlIHBsb3RzDQpsaWJyYXJ5KERUKSAgICAgICAgICAgICAjIEludGVyYWN0aXZlIGRhdGEgdGFibGVzDQpsaWJyYXJ5KFJDb2xvckJyZXdlcikgICAjIENvbG9yIHBhbGV0dGVzDQoNCiMgU2V0IGdsb2JhbCBwbG90dGluZyBwYXJhbWV0ZXJzIGZvciBCYXNlIFINCnBhcihtYXIgPSBjKDUsIDQsIDQsIDIpICsgMC4xKSAgIyBTZXQgbWFyZ2luczogYm90dG9tLCBsZWZ0LCB0b3AsIHJpZ2h0DQpgYGANCg0KIyBEYXRhIFZpc3VhbGl6YXRpb24gVHV0b3JpYWwgaW4gUg0KDQojIyBXZWxjb21lIHRvIHRoZSBCaWcgRGF0YSBBbmFseXNpcyBhbmQgTWFuYWdlbWVudCBUcmFpbmluZyBQcm9ncmFtDQoNCldlbGNvbWUgdG8gdGhlIHNlY29uZCBzZWN0aW9uIG9mIG91ciBjb21wcmVoZW5zaXZlIHRyYWluaW5nIHByb2dyYW0hIFRoaXMgc2VjdGlvbiBwcm92aWRlcyBhIGNvbXByZWhlbnNpdmUgZ3VpZGUgdG8gZGF0YSB2aXN1YWxpemF0aW9uIGluIFIgdXNpbmcgdGhyZWUgYXBwcm9hY2hlczoNCg0KMS4gKipCYXNlIFIgR3JhcGhpY3MqKiAtIFF1aWNrIGV4cGxvcmF0b3J5IHBsb3RzDQoyLiAqKmdncGxvdDIqKiAtIFB1YmxpY2F0aW9uLXF1YWxpdHkgcGxvdHMNCjMuICoqUGxvdGx5KiogLSBJbnRlcmFjdGl2ZSB3ZWItYmFzZWQgcGxvdHMNCg0KRWFjaCBzZWN0aW9uIHByb2dyZXNzZXMgZnJvbSBzaW1wbGUgdG8gY29tcGxleCBleGFtcGxlcyB3aXRoIGRldGFpbGVkIGV4cGxhbmF0aW9ucyBvZiBmdW5jdGlvbiBhcmd1bWVudHMuDQoNCi0tLQ0KDQojIFNlY3Rpb24gMTogQkFTRSBSIEdSQVBISUNTDQoNCiMjIDEuMSBCYXNpYyBQbG90IEZ1bmN0aW9uDQoNCmBgYHtyIGJhc2UtcGxvdC1pbnRyb30NCiMgTG9hZCBidWlsdC1pbiBkYXRhc2V0DQpkYXRhKG10Y2FycykNCg0KIyBEaXNwbGF5IGRhdGFzZXQgaW5mb3JtYXRpb24NCmNhdCgNCiAgIkRhdGFzZXQ6IG10Y2FycyAoTW90b3IgVHJlbmQgQ2FyIFJvYWQgVGVzdHMpXG4iLA0KICAiUm93czoiLCBucm93KG10Y2FycyksICJ8IENvbHVtbnM6IiwgbmNvbChtdGNhcnMpLCAiXG5cbiIsDQogICJGaXJzdCA2IHJvd3M6XG4iLA0KICBzZXAgPSAiIg0KKQ0KcHJpbnQoaGVhZChtdGNhcnMpKQ0KYGBgDQoNCiMjIDEuMiBTaW1wbGUgQmxhY2sgJiBXaGl0ZSBTY2F0dGVyIFBsb3QNCg0KYGBge3IgYmFzZS1zY2F0dGVyLWJ3LCBmaWcuaGVpZ2h0PTV9DQojIEJhc2ljIHNjYXR0ZXIgcGxvdCB3aXRoIG1pbmltYWwgYXJndW1lbnRzDQpwbG90KG10Y2FycyR3dCwgICAgICAgICAgICMgeCB2YXJpYWJsZTogY2FyIHdlaWdodA0KICAgICBtdGNhcnMkbXBnLCAgICAgICAgICAjIHkgdmFyaWFibGU6IG1pbGVzIHBlciBnYWxsb24NCiAgICAgbWFpbiA9ICJXZWlnaHQgdnMgTVBHIiwgICMgbWFpbjogcGxvdCB0aXRsZQ0KICAgICB4bGFiID0gIldlaWdodCAoMTAwMCBsYnMpIiwgICMgeGxhYjogeC1heGlzIGxhYmVsDQogICAgIHlsYWIgPSAiTWlsZXMgcGVyIEdhbGxvbiIsICAgIyB5bGFiOiB5LWF4aXMgbGFiZWwNCiAgICAgcGNoID0gMTYsICAgICAgICAgICAgIyBwY2g6IHBvaW50IGNoYXJhY3RlciAoMTYgPSBmaWxsZWQgY2lyY2xlKQ0KICAgICBjb2wgPSAiYmxhY2siLCAgICAgICAjIGNvbDogcG9pbnQgY29sb3INCiAgICAgY2V4ID0gMS4wLCAgICAgICAgICAgIyBjZXg6IGNoYXJhY3RlciBleHBhbnNpb24gKHBvaW50IHNpemUpDQogICAgIGZyYW1lID0gRkFMU0UpICAgICAgICMgZnJhbWU6IHJlbW92ZSBib3ggYXJvdW5kIHBsb3QNCmBgYA0KDQojIyAxLjMgQWRkaW5nIENvbG9yIGFuZCBDdXN0b21pemF0aW9uDQoNCmBgYHtyIGJhc2Utc2NhdHRlci1jb2xvciwgZmlnLmhlaWdodD01fQ0KIyBDcmVhdGUgY29sb3IgdmVjdG9yIGJhc2VkIG9uIG51bWJlciBvZiBjeWxpbmRlcnMNCmNvbG9ycyA8LSBpZmVsc2UobXRjYXJzJGN5bCA9PSA0LCAicmVkIiwNCiAgICAgICAgICAgICAgICBpZmVsc2UobXRjYXJzJGN5bCA9PSA2LCAiZ3JlZW4iLCAiYmx1ZSIpKQ0KDQojIEVuaGFuY2VkIHNjYXR0ZXIgcGxvdCB3aXRoIGNvbG9ycyBhbmQgc2l6ZXMNCnBsb3QobXRjYXJzJHd0LCBtdGNhcnMkbXBnLA0KICAgICBtYWluID0gIldlaWdodCB2cyBNUEcgKENvbG9yZWQgYnkgQ3lsaW5kZXJzKSIsDQogICAgIHhsYWIgPSAiV2VpZ2h0ICgxMDAwIGxicykiLA0KICAgICB5bGFiID0gIk1pbGVzIHBlciBHYWxsb24iLA0KICAgICBwY2ggPSBpZmVsc2UobXRjYXJzJGN5bCA9PSA0LCAxNiwgICAgICAjIERpZmZlcmVudCBzaGFwZXMgZm9yIGRpZmZlcmVudCBjeWxpbmRlcnMNCiAgICAgICAgICAgICAgICAgIGlmZWxzZShtdGNhcnMkY3lsID09IDYsIDE3LCAxOCkpLA0KICAgICBjb2wgPSBjb2xvcnMsICAgICAgICAjIFVzZSBjdXN0b20gY29sb3IgdmVjdG9yDQogICAgIGNleCA9IGlmZWxzZShtdGNhcnMkY3lsID09IDQsIDEuMCwgICAgICMgRGlmZmVyZW50IHNpemVzIGJhc2VkIG9uIGN5bGluZGVycw0KICAgICAgICAgICAgICAgICAgaWZlbHNlKG10Y2FycyRjeWwgPT0gNiwgMS4zLCAxLjYpKSwNCiAgICAgZnJhbWUgPSBGQUxTRSwNCiAgICAgbHdkID0gMS41KSAgICAgICAgICAgIyBsd2Q6IGxpbmUgd2lkdGggZm9yIHBvaW50IGJvcmRlcnMNCg0KIyBBZGQgbGVnZW5kDQpsZWdlbmQoInRvcHJpZ2h0IiwgICAgICAgICAgICAgICAgICAgICAjIFBvc2l0aW9uOiB0b3AgcmlnaHQgY29ybmVyDQogICAgICAgbGVnZW5kID0gYygiNCBDeWxpbmRlcnMiLCAiNiBDeWxpbmRlcnMiLCAiOCBDeWxpbmRlcnMiKSwgICMgVGV4dCBsYWJlbHMNCiAgICAgICBjb2wgPSBjKCJyZWQiLCAiZ3JlZW4iLCAiYmx1ZSIpLCAjIENvbG9ycw0KICAgICAgIHBjaCA9IGMoMTYsIDE3LCAxOCksICAgICAgICAgICAgIyBQb2ludCBjaGFyYWN0ZXJzDQogICAgICAgcHQuY2V4ID0gYygxLjAsIDEuMywgMS42KSwgICAgICAjIFBvaW50IHNpemVzDQogICAgICAgdGl0bGUgPSAiQ3lsaW5kZXJzIiwgICAgICAgICAgICAjIExlZ2VuZCB0aXRsZQ0KICAgICAgIGJ0eSA9ICJuIikgICAgICAgICAgICAgICAgICAgICAgIyBidHk6IGJveCB0eXBlICgibiIgPSBubyBib3gpDQpgYGANCg0KIyMgMS40IEFkZGluZyBSZWdyZXNzaW9uIExpbmUgYW5kIEdyaWQNCg0KYGBge3IgYmFzZS1zY2F0dGVyLWVuaGFuY2VkLCBmaWcuaGVpZ2h0PTV9DQojIFBsb3Qgd2l0aCByZWdyZXNzaW9uIGxpbmUgYW5kIGdyaWQNCnBsb3QobXRjYXJzJHd0LCBtdGNhcnMkbXBnLA0KICAgICBtYWluID0gIldlaWdodCB2cyBNUEcgd2l0aCBSZWdyZXNzaW9uIExpbmUiLA0KICAgICB4bGFiID0gIldlaWdodCAoMTAwMCBsYnMpIiwNCiAgICAgeWxhYiA9ICJNaWxlcyBwZXIgR2FsbG9uIiwNCiAgICAgcGNoID0gMTYsDQogICAgIGNvbCA9IHJnYigwLjIsIDAuNCwgMC44LCAwLjcpLCAgIyBSR0IgY29sb3JzIHdpdGggdHJhbnNwYXJlbmN5IChhbHBoYSA9IDAuNykNCiAgICAgY2V4ID0gMS4yLA0KICAgICBmcmFtZSA9IEZBTFNFKQ0KDQojIEFkZCBncmlkIGxpbmVzDQpncmlkKGNvbCA9ICJncmF5IiwgICAgICAgICMgR3JpZCBjb2xvcg0KICAgICBsdHkgPSAiZG90dGVkIiwgICAgICAjIGx0eTogbGluZSB0eXBlICgiZG90dGVkIiwgImRhc2hlZCIsICJzb2xpZCIpDQogICAgIGx3ZCA9IDAuNSkgICAgICAgICAgICMgbHdkOiBsaW5lIHdpZHRoDQoNCiMgQWRkIHJlZ3Jlc3Npb24gbGluZSB1c2luZyBhYmxpbmUoKQ0KYWJsaW5lKGxtKG1wZyB+IHd0LCBkYXRhID0gbXRjYXJzKSwgICMgTGluZWFyIG1vZGVsDQogICAgICAgY29sID0gInJlZCIsICAgICAgICAgICAgICAgICAgICMgTGluZSBjb2xvcg0KICAgICAgIGx3ZCA9IDIsICAgICAgICAgICAgICAgICAgICAgICAjIExpbmUgd2lkdGgNCiAgICAgICBsdHkgPSAiZGFzaGVkIikgICAgICAgICAgICAgICAgIyBMaW5lIHR5cGU6IGRhc2hlZA0KDQojIEFkZCB0ZXh0IGFubm90YXRpb24NCnRleHQoeCA9IDQuNSwgeSA9IDMwLCAgICAgICAgICAgICAgICAjIHgsIHk6IGNvb3JkaW5hdGVzIGZvciB0ZXh0DQogICAgIGxhYmVscyA9ICJOZWdhdGl2ZSBDb3JyZWxhdGlvbiIsICMgVGV4dCB0byBkaXNwbGF5DQogICAgIGNvbCA9ICJkYXJrcmVkIiwgICAgICAgICAgICAgICAgICMgVGV4dCBjb2xvcg0KICAgICBjZXggPSAxLjEpICAgICAgICAgICAgICAgICAgICAgICAjIFRleHQgc2l6ZQ0KYGBgDQoNCiMjIDEuNSBVc2luZyBwYXIoKSBmb3IgTXVsdGlwbGUgUGxvdHMNCg0KYGBge3IgcGFyLW11bHRpcGxlLCBmaWcuaGVpZ2h0PTh9DQojIFNhdmUgY3VycmVudCBwYXIgc2V0dGluZ3MNCm9sZF9wYXIgPC0gcGFyKCkNCg0KIyBTZXQgdXAgMngyIHBsb3QgZ3JpZA0KcGFyKG1mcm93ID0gYygyLCAyKSwgICAgICAgICMgbWZyb3c6IG1hdHJpeCBvZiBwbG90cyAocm93cywgY29sdW1ucykNCiAgICBtYXIgPSBjKDQsIDQsIDMsIDEpLCAgICAjIG1hcjogbWFyZ2lucyAoYm90dG9tLCBsZWZ0LCB0b3AsIHJpZ2h0KQ0KICAgIG9tYSA9IGMoMiwgMiwgMiwgMCkpICAgICMgb21hOiBvdXRlciBtYXJnaW5zDQoNCiMgUGxvdCAxOiBCYXNpYyBzY2F0dGVyDQpwbG90KG10Y2FycyR3dCwgbXRjYXJzJG1wZywNCiAgICAgbWFpbiA9ICJCYXNpYyBTY2F0dGVyIiwNCiAgICAgeGxhYiA9ICJXZWlnaHQiLA0KICAgICB5bGFiID0gIk1QRyIsDQogICAgIHBjaCA9IDE2LA0KICAgICBjb2wgPSAic3RlZWxibHVlIikNCg0KIyBQbG90IDI6IFdpdGggcmVncmVzc2lvbiBsaW5lDQpwbG90KG10Y2FycyR3dCwgbXRjYXJzJG1wZywNCiAgICAgbWFpbiA9ICJXaXRoIFJlZ3Jlc3Npb24iLA0KICAgICB4bGFiID0gIldlaWdodCIsDQogICAgIHlsYWIgPSAiTVBHIiwNCiAgICAgcGNoID0gMTYsDQogICAgIGNvbCA9ICJmb3Jlc3RncmVlbiIpDQphYmxpbmUobG0obXBnIH4gd3QsIGRhdGEgPSBtdGNhcnMpLCBjb2wgPSAicmVkIiwgbHdkID0gMikNCg0KIyBQbG90IDM6IENvbG9yIGJ5IGN5bGluZGVycw0KY29sb3JzIDwtIGMoInJlZCIsICJncmVlbiIsICJibHVlIilbYXMuZmFjdG9yKG10Y2FycyRjeWwpXQ0KcGxvdChtdGNhcnMkd3QsIG10Y2FycyRtcGcsDQogICAgIG1haW4gPSAiQnkgQ3lsaW5kZXJzIiwNCiAgICAgeGxhYiA9ICJXZWlnaHQiLA0KICAgICB5bGFiID0gIk1QRyIsDQogICAgIHBjaCA9IDE2LA0KICAgICBjb2wgPSBjb2xvcnMpDQoNCiMgUGxvdCA0OiBXaXRoIHNtb290aCBjdXJ2ZQ0KcGxvdChtdGNhcnMkd3QsIG10Y2FycyRtcGcsDQogICAgIG1haW4gPSAiV2l0aCBTbW9vdGggQ3VydmUiLA0KICAgICB4bGFiID0gIldlaWdodCIsDQogICAgIHlsYWIgPSAiTVBHIiwNCiAgICAgcGNoID0gMTYsDQogICAgIGNvbCA9ICJwdXJwbGUiKQ0KbGluZXMobG93ZXNzKG10Y2FycyR3dCwgbXRjYXJzJG1wZyksICAjIExPV0VTUyBzbW9vdGhlcg0KICAgICAgY29sID0gIm9yYW5nZSIsDQogICAgICBsd2QgPSAyKQ0KDQojIEFkZCBvdmVyYWxsIHRpdGxlDQptdGV4dCgiTXVsdGlwbGUgVmlld3Mgb2YgV2VpZ2h0IHZzIE1QRyIsICAjIFRleHQgdG8gZGlzcGxheQ0KICAgICAgc2lkZSA9IDMsICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBzaWRlOiAzID0gdG9wDQogICAgICBvdXRlciA9IFRSVUUsICAgICAgICAgICAgICAgICAgICAgICAjIFBsYWNlIGluIG91dGVyIG1hcmdpbg0KICAgICAgY2V4ID0gMS41LCAgICAgICAgICAgICAgICAgICAgICAgICAgIyBUZXh0IHNpemUNCiAgICAgIGZvbnQgPSAyKSAgICAgICAgICAgICAgICAgICAgICAgICAgICMgRm9udDogMiA9IGJvbGQNCg0KIyBSZXNldCB0byBvcmlnaW5hbCBwYXIgc2V0dGluZ3MNCnBhcihvbGRfcGFyKQ0KYGBgDQoNCiMjIDEuNiBIaXN0b2dyYW1zIHdpdGggQmFzZSBSDQoNCmBgYHtyIGJhc2UtaGlzdG9ncmFtLCBmaWcuaGVpZ2h0PTZ9DQojIExvYWQgZmFpdGhmdWwgZGF0YXNldA0KZGF0YShmYWl0aGZ1bCkNCg0KIyBEaXNwbGF5IGRhdGFzZXQgaW5mbw0KY2F0KCJEYXRhc2V0OiBmYWl0aGZ1bCAoT2xkIEZhaXRoZnVsIEdleXNlcilcbiIpDQpjYXQoIlJvd3M6IiwgbnJvdyhmYWl0aGZ1bCksICJcblxuIikNCg0KcHJpbnQoaGVhZChmYWl0aGZ1bCkpDQoNCiMgU2V0IHVwIDF4MiBwbG90IGxheW91dA0KcGFyKG1mcm93ID0gYygxLCAyKSkNCg0KIyBCYXNpYyBoaXN0b2dyYW0NCmhpc3QoZmFpdGhmdWwkd2FpdGluZywgICAgICAgICAgIyBEYXRhIHZlY3Rvcg0KICAgICBtYWluID0gIkJhc2ljIEhpc3RvZ3JhbSIsICAjIFRpdGxlDQogICAgIHhsYWIgPSAiV2FpdGluZyBUaW1lIChtaW4pIiwgIyB4LWF4aXMgbGFiZWwNCiAgICAgeWxhYiA9ICJGcmVxdWVuY3kiLCAgICAgICAgIyB5LWF4aXMgbGFiZWwNCiAgICAgY29sID0gImxpZ2h0Ymx1ZSIsICAgICAgICAgIyBGaWxsIGNvbG9yDQogICAgIGJvcmRlciA9ICJ3aGl0ZSIsICAgICAgICAgICMgQm9yZGVyIGNvbG9yDQogICAgIGJyZWFrcyA9IDE1LCAgICAgICAgICAgICAgICMgTnVtYmVyIG9mIGJpbnMNCiAgICAgZnJlcSA9IFRSVUUpICAgICAgICAgICAgICAgIyBmcmVxOiBUUlVFIGZvciBmcmVxdWVuY3ksIEZBTFNFIGZvciBkZW5zaXR5DQoNCiMgSGlzdG9ncmFtIHdpdGggZGVuc2l0eSBjdXJ2ZQ0KaGlzdChmYWl0aGZ1bCR3YWl0aW5nLA0KICAgICBtYWluID0gIldpdGggRGVuc2l0eSBDdXJ2ZSIsDQogICAgIHhsYWIgPSAiV2FpdGluZyBUaW1lIChtaW4pIiwNCiAgICAgeWxhYiA9ICJEZW5zaXR5IiwNCiAgICAgY29sID0gcmdiKDAuOCwgMC45LCAxLCAwLjYpLCAgIyBMaWdodCBibHVlIHdpdGggdHJhbnNwYXJlbmN5DQogICAgIGJvcmRlciA9ICJuYXZ5IiwNCiAgICAgZnJlcSA9IEZBTFNFLCAgICAgICAgICAgICAgICAgICMgUGxvdCBkZW5zaXR5IGluc3RlYWQgb2YgZnJlcXVlbmN5DQogICAgIGJyZWFrcyA9IDIwKQ0KDQojIEFkZCBkZW5zaXR5IGN1cnZlDQpsaW5lcyhkZW5zaXR5KGZhaXRoZnVsJHdhaXRpbmcpLCAgIyBEZW5zaXR5IGVzdGltYXRpb24NCiAgICAgIGNvbCA9ICJkYXJrcmVkIiwgICAgICAgICAgICAjIExpbmUgY29sb3INCiAgICAgIGx3ZCA9IDIpICAgICAgICAgICAgICAgICAgICAjIExpbmUgd2lkdGgNCg0KIyBBZGQgcnVnIHBsb3QgKHNob3dzIGluZGl2aWR1YWwgZGF0YSBwb2ludHMpDQpydWcoZmFpdGhmdWwkd2FpdGluZywgICAgICAgICAgICAgIyBEYXRhIHBvaW50cw0KICAgIHNpZGUgPSAxLCAgICAgICAgICAgICAgICAgICAgICMgc2lkZTogMSA9IGJvdHRvbQ0KICAgIGNvbCA9ICJyZWQiLCAgICAgICAgICAgICAgICAgICMgQ29sb3INCiAgICBsd2QgPSAwLjUpICAgICAgICAgICAgICAgICAgICAjIExpbmUgd2lkdGgNCg0KIyBSZXNldCBsYXlvdXQNCnBhcihtZnJvdyA9IGMoMSwgMSkpDQpgYGANCg0KIyMgMS43IEJveCBQbG90cw0KDQpgYGB7ciBiYXNlLWJveHBsb3QsIGZpZy5oZWlnaHQ9Nn0NCiMgTG9hZCBUb290aEdyb3d0aCBkYXRhc2V0DQpkYXRhKFRvb3RoR3Jvd3RoKQ0KDQojIERpc3BsYXkgZGF0YXNldCBpbmZvDQpjYXQoIkRhdGFzZXQ6IFRvb3RoR3Jvd3RoXG4iKQ0KY2F0KCJSb3dzOiIsIG5yb3coVG9vdGhHcm93dGgpLCAiXG4iKQ0KcHJpbnQoaGVhZChUb290aEdyb3d0aCkpDQoNCiMgQ3JlYXRlIGJveCBwbG90DQpib3hwbG90KGxlbiB+IHN1cHAsICAgICAgICAgICAgIyBGb3JtdWxhOiBsZW5ndGggYnkgc3VwcGxlbWVudCB0eXBlDQogICAgICAgIGRhdGEgPSBUb290aEdyb3d0aCwgICAgIyBEYXRhIHNvdXJjZQ0KICAgICAgICBtYWluID0gIlRvb3RoIEdyb3d0aCBieSBTdXBwbGVtZW50IiwgICMgVGl0bGUNCiAgICAgICAgeGxhYiA9ICJTdXBwbGVtZW50IFR5cGUiLCAgICAgICAgICAgICAjIHgtYXhpcyBsYWJlbA0KICAgICAgICB5bGFiID0gIlRvb3RoIExlbmd0aCIsICAgICAgICAgICAgICAgICMgeS1heGlzIGxhYmVsDQogICAgICAgIGNvbCA9IGMoImxpZ2h0Ymx1ZSIsICJsaWdodGdyZWVuIiksICAgIyBDb2xvcnMgZm9yIGdyb3Vwcw0KICAgICAgICBib3JkZXIgPSAiZGFya2JsdWUiLCAgICAgICAgICAgICAgICAgICMgQm94IGJvcmRlciBjb2xvcg0KICAgICAgICBub3RjaCA9IFRSVUUsICAgICAgICAgICAgICAgICAgICAgICAgICMgbm90Y2g6IGFkZCBub3RjaGVzIGZvciBtZWRpYW4gY29tcGFyaXNvbg0KICAgICAgICBvdXRwY2ggPSAxNiwgICAgICAgICAgICAgICAgICAgICAgICAgICMgb3V0cGNoOiBvdXRsaWVyIHBvaW50IGNoYXJhY3Rlcg0KICAgICAgICBvdXRjb2wgPSAicmVkIiwgICAgICAgICAgICAgICAgICAgICAgICMgb3V0Y29sOiBvdXRsaWVyIGNvbG9yDQogICAgICAgIG91dGNleCA9IDEuMikgICAgICAgICAgICAgICAgICAgICAgICAgIyBvdXRjZXg6IG91dGxpZXIgc2l6ZQ0KDQojIEFkZCBwb2ludHMgZm9yIGluZGl2aWR1YWwgZGF0YQ0Kc3RyaXBjaGFydChsZW4gfiBzdXBwLCAgICAgICAgICAjIEZvcm11bGENCiAgICAgICAgICAgZGF0YSA9IFRvb3RoR3Jvd3RoLCAgIyBEYXRhDQogICAgICAgICAgIHZlcnRpY2FsID0gVFJVRSwgICAgICMgdmVydGljYWw6IFRSVUUgZm9yIHZlcnRpY2FsIG9yaWVudGF0aW9uDQogICAgICAgICAgIG1ldGhvZCA9ICJqaXR0ZXIiLCAgICMgbWV0aG9kOiAiaml0dGVyIiB0byBzcHJlYWQgcG9pbnRzDQogICAgICAgICAgIHBjaCA9IDE2LCAgICAgICAgICAgICMgUG9pbnQgY2hhcmFjdGVyDQogICAgICAgICAgIGNvbCA9IHJnYigwLCAwLCAwLCAwLjMpLCAgIyBTZW1pLXRyYW5zcGFyZW50IGJsYWNrDQogICAgICAgICAgIGNleCA9IDAuOCwgICAgICAgICAgICMgUG9pbnQgc2l6ZQ0KICAgICAgICAgICBhZGQgPSBUUlVFKSAgICAgICAgICAjIGFkZDogYWRkIHRvIGV4aXN0aW5nIHBsb3QNCmBgYA0KDQojIyAxLjggVGltZSBTZXJpZXMgUGxvdA0KDQpgYGB7ciBiYXNlLXRpbWVzZXJpZXMsIGZpZy5oZWlnaHQ9NX0NCiMgTG9hZCBBaXJQYXNzZW5nZXJzIGRhdGFzZXQNCmRhdGEoQWlyUGFzc2VuZ2VycykNCg0KIyBEaXNwbGF5IGRhdGFzZXQgaW5mbw0KY2F0KCJEYXRhc2V0OiBBaXJQYXNzZW5nZXJzIChNb250aGx5IHRvdGFscyAxOTQ5LTE5NjApXG4iKQ0KcHJpbnQoc3RyKEFpclBhc3NlbmdlcnMpKQ0KcHJpbnQoaGVhZChBaXJQYXNzZW5nZXJzKSkNCg0KDQojIEJhc2ljIHRpbWUgc2VyaWVzIHBsb3QNCnBsb3QoQWlyUGFzc2VuZ2VycywgICAgICAgICAgICAgIyBUaW1lIHNlcmllcyBvYmplY3QNCiAgICAgbWFpbiA9ICJBaXJsaW5lIFBhc3NlbmdlcnMgT3ZlciBUaW1lIiwgICMgVGl0bGUNCiAgICAgeGxhYiA9ICJZZWFyIiwgICAgICAgICAgICAgICAgICAgICAgICAgIyB4LWF4aXMgbGFiZWwNCiAgICAgeWxhYiA9ICJQYXNzZW5nZXJzICh0aG91c2FuZHMpIiwgICAgICAgIyB5LWF4aXMgbGFiZWwNCiAgICAgdHlwZSA9ICJsIiwgICAgICAgICAgICAgICAgIyB0eXBlOiAibCIgPSBsaW5lIHBsb3QNCiAgICAgY29sID0gImJsdWUiLCAgICAgICAgICAgICAgIyBMaW5lIGNvbG9yDQogICAgIGx3ZCA9IDIsICAgICAgICAgICAgICAgICAgICMgTGluZSB3aWR0aA0KICAgICBsYXMgPSAxKSAgICAgICAgICAgICAgICAgICAjIGxhczogYXhpcyBsYWJlbCBzdHlsZSAoMSA9IGhvcml6b250YWwpDQoNCiMgQWRkIHNlYXNvbmFsIGRlY29tcG9zaXRpb24gbGluZXMNCmRlY29tcCA8LSBkZWNvbXBvc2UoQWlyUGFzc2VuZ2VycykNCmxpbmVzKGRlY29tcCR0cmVuZCwgICAgICAgICAgICAgIyBUcmVuZCBjb21wb25lbnQNCiAgICAgIGNvbCA9ICJyZWQiLCAgICAgICAgICAgICAgIyBDb2xvcg0KICAgICAgbHdkID0gMiwgICAgICAgICAgICAgICAgICAjIExpbmUgd2lkdGgNCiAgICAgIGx0eSA9ICJkYXNoZWQiKSAgICAgICAgICAgIyBMaW5lIHR5cGUNCg0KIyBBZGQgbGVnZW5kDQpsZWdlbmQoInRvcGxlZnQiLCAgICAgICAgICAgICAgICMgUG9zaXRpb24NCiAgICAgICBsZWdlbmQgPSBjKCJPcmlnaW5hbCIsICJUcmVuZCIpLCAgIyBMYWJlbHMNCiAgICAgICBjb2wgPSBjKCJibHVlIiwgInJlZCIpLCAgIyBDb2xvcnMNCiAgICAgICBsd2QgPSBjKDIsIDIpLCAgICAgICAgICAgIyBMaW5lIHdpZHRocw0KICAgICAgIGx0eSA9IGMoInNvbGlkIiwgImRhc2hlZCIpLCAgIyBMaW5lIHR5cGVzDQogICAgICAgYnR5ID0gIm4iKSAgICAgICAgICAgICAgICMgTm8gYm94IGFyb3VuZCBsZWdlbmQNCmBgYA0KDQojIyAxLjkgQmFzZSBSIEV4ZXJjaXNlcw0KDQpgYGB7ciBiYXNlLWV4ZXJjaXNlLWluc3RydWN0aW9ucywgZXZhbD1GQUxTRX0NCiMgRVhFUkNJU0UgMTogQ3JlYXRlIGEgc2NhdHRlciBwbG90IG9mIFNlcGFsLkxlbmd0aCB2cyBTZXBhbC5XaWR0aCBmcm9tIGlyaXMgZGF0YXNldA0KIyBSZXF1aXJlbWVudHM6DQojIDEuIENvbG9yIHBvaW50cyBieSBTcGVjaWVzDQojIDIuIEFkZCBhIGxlZ2VuZA0KIyAzLiBBZGQgYSB0aXRsZSBhbmQgYXhpcyBsYWJlbHMNCiMgNC4gQWRkIGEgZ3JpZA0KDQojIEVYRVJDSVNFIDI6IENyZWF0ZSBhIGhpc3RvZ3JhbSBvZiBQZXRhbC5MZW5ndGggZnJvbSBpcmlzIGRhdGFzZXQNCiMgUmVxdWlyZW1lbnRzOg0KIyAxLiBVc2UgZGlmZmVyZW50IGNvbG9ycyBmb3IgZWFjaCBTcGVjaWVzDQojIDIuIEFkZCBkZW5zaXR5IGN1cnZlcw0KIyAzLiBBZGQgYXBwcm9wcmlhdGUgdGl0bGUgYW5kIGxhYmVscw0KDQojIEVYRVJDSVNFIDM6IENyZWF0ZSBhIDJ4MiBwbG90IG1hdHJpeCBzaG93aW5nOg0KIyAxLiBCb3ggcGxvdCBvZiBtcGcgYnkgY3lsaW5kZXIgY291bnQNCiMgMi4gSGlzdG9ncmFtIG9mIG1wZw0KIyAzLiBTY2F0dGVyIHBsb3Qgb2YgaHAgdnMgbXBnDQojIDQuIEJhciBwbG90IG9mIGN5bGluZGVyIGNvdW50cw0KYGBgDQoNCi0tLQ0KDQojIFNlY3Rpb24gMjogR0dQTE9UMiBWSVNVQUxJWkFUSU9ODQpgYGB7ciBpcmlzfQ0KcHJpbnQoc3RyKGlyaXMpKQ0KcHJpbnQoaGVhZChpcmlzKSkNCmBgYA0KDQojIyAyLjEgSW50cm9kdWN0aW9uIHRvIGdncGxvdDINCg0KZ2dwbG90MiBpcyBiYXNlZCBvbiB0aGUgIkdyYW1tYXIgb2YgR3JhcGhpY3MiIC0gYSBzeXN0ZW1hdGljIGFwcHJvYWNoIHRvIGJ1aWxkaW5nIHBsb3RzIGxheWVyIGJ5IGxheWVyLg0KDQpgYGB7ciBnZ3Bsb3QtaW50cm99DQojIEJhc2ljIGdncGxvdDIgc3ludGF4IHN0cnVjdHVyZQ0KY2F0KCJnZ3Bsb3QyIEJhc2ljIFN5bnRheDpcbiIsDQogICAgImdncGxvdChkYXRhLCBhZXMoeCwgeSkpICsgICAgICAgICAgICMgSW5pdGlhbGl6ZSBwbG90XG4iLA0KICAgICIgIGdlb21fbGF5ZXIoKSArICAgICAgICAgICAgICAgICAgICAjIEFkZCBnZW9tZXRyeVxuIiwNCiAgICAiICBzY2FsZV8qKCkgKyAgICAgICAgICAgICAgICAgICAgICAgIyBDdXN0b21pemUgc2NhbGVzXG4iLA0KICAgICIgIHRoZW1lXyooKSArICAgICAgICAgICAgICAgICAgICAgICAjIEFwcGx5IHRoZW1lXG4iLA0KICAgICIgIGxhYnMoKSAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIEFkZCBsYWJlbHNcbiIsDQogICAgc2VwID0gIiIpDQoNCmBgYA0KDQojIyAyLjIgQmFzaWMgZ2dwbG90IFNjYXR0ZXIgUGxvdA0KDQpgYGB7ciBnZ3Bsb3Qtc2NhdHRlci1iYXNpYywgZmlnLmhlaWdodD01fQ0KIyBMb2FkIGlyaXMgZGF0YXNldA0KZGF0YShpcmlzKQ0KDQojIEJhc2ljIGdncGxvdCBzY2F0dGVyIHBsb3QNCmdncGxvdChpcmlzLCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIGRhdGE6IGRhdGFzZXQNCiAgICAgICBhZXMoeCA9IFNlcGFsLkxlbmd0aCwgICAgICAgICAgICAgICAjIGFlczogYWVzdGhldGljIG1hcHBpbmdzDQogICAgICAgICAgIHkgPSBTZXBhbC5XaWR0aCwgICAgICAgICAgICAgICAgIyB4IGFuZCB5IHZhcmlhYmxlcw0KICAgICAgICAgICBjb2xvciA9IFNwZWNpZXMpKSArICAgICAgICAgICAgICMgY29sb3I6IG1hcCBTcGVjaWVzIHRvIGNvbG9yDQogIGdlb21fcG9pbnQoc2l6ZSA9IDMsICAgICAgICAgICAgICAgICAgICAgIyBnZW9tX3BvaW50OiBzY2F0dGVyIHBsb3QgbGF5ZXINCiAgICAgICAgICAgICBhbHBoYSA9IDAuNykgKyAgICAgICAgICAgICAgICAjIGFscGhhOiB0cmFuc3BhcmVuY3kgKDAtMSkNCiAgbGFicyh0aXRsZSA9ICJTZXBhbCBMZW5ndGggdnMgV2lkdGgiLCAgICAjIGxhYnM6IGxhYmVscyBhbmQgdGl0bGVzDQogICAgICAgc3VidGl0bGUgPSAiSXJpcyBEYXRhc2V0IiwNCiAgICAgICB4ID0gIlNlcGFsIExlbmd0aCAoY20pIiwNCiAgICAgICB5ID0gIlNlcGFsIFdpZHRoIChjbSkiLA0KICAgICAgIGNvbG9yID0gIlNwZWNpZXMiKSArICAgICAgICAgICAgICAgICMgTGVnZW5kIHRpdGxlDQogIHRoZW1lX21pbmltYWwoKSArICAgICAgICAgICAgICAgICAgICAgICAgIyB0aGVtZTogbWluaW1hbCB0aGVtZQ0KICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41KSwgICAgIyBDZW50ZXIgdGl0bGUNCiAgICAgICAgcGxvdC5zdWJ0aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSkpICMgQ2VudGVyIHN1YnRpdGxlDQpgYGANCg0KIyMgMi4zIGdncGxvdCB3aXRoIE11bHRpcGxlIEdlb21ldHJpZXMNCg0KYGBge3IgZ2dwbG90LW11bHRpcGxlLWdlb21zLCBmaWcuaGVpZ2h0PTV9DQojIFBsb3Qgd2l0aCBtdWx0aXBsZSBnZW9tZXRyeSBsYXllcnMNCmdncGxvdChpcmlzLCBhZXMoeCA9IFNlcGFsLkxlbmd0aCwgeSA9IFNlcGFsLldpZHRoLCBjb2xvciA9IFNwZWNpZXMpKSArDQogIGdlb21fcG9pbnQoc2l6ZSA9IDMsICAgICAgICAgICAgICAgICAgICAjIEZpcnN0IGxheWVyOiBwb2ludHMNCiAgICAgICAgICAgICBhbHBoYSA9IDAuNikgKw0KICBnZW9tX3Ntb290aChtZXRob2QgPSAibG0iLCAgICAgICAgICAgICAgIyBTZWNvbmQgbGF5ZXI6IGxpbmVhciByZWdyZXNzaW9uDQogICAgICAgICAgICAgIHNlID0gVFJVRSwgICAgICAgICAgICAgICAgICAjIHNlOiBzaG93IGNvbmZpZGVuY2UgaW50ZXJ2YWwNCiAgICAgICAgICAgICAgZm9ybXVsYSA9IHkgfiB4LCAgICAgICAgICAgICMgRm9ybXVsYSBmb3Igc21vb3RoaW5nDQogICAgICAgICAgICAgIGFscGhhID0gMC4yKSArICAgICAgICAgICAgICAjIFRyYW5zcGFyZW5jeSBmb3IgY29uZmlkZW5jZSBiYW5kDQogIGdlb21fZGVuc2l0eV8yZChhbHBoYSA9IDAuNSwgICAgICAgICAgICAjIFRoaXJkIGxheWVyOiAyRCBkZW5zaXR5IGNvbnRvdXJzDQogICAgICAgICAgICAgICAgICBjb2xvciA9ICJibGFjayIpICsNCiAgZmFjZXRfd3JhcCh+IFNwZWNpZXMsICAgICAgICAgICAgICAgICAgICMgZmFjZXRfd3JhcDogc2VwYXJhdGUgcGxvdHMgYnkgU3BlY2llcw0KICAgICAgICAgICAgIG5jb2wgPSAzKSArICAgICAgICAgICAgICAgICAgIyBuY29sOiBudW1iZXIgb2YgY29sdW1ucw0KICBsYWJzKHRpdGxlID0gIlNlcGFsIERpbWVuc2lvbnMgd2l0aCBSZWdyZXNzaW9uIiwNCiAgICAgICB4ID0gIlNlcGFsIExlbmd0aCAoY20pIiwNCiAgICAgICB5ID0gIlNlcGFsIFdpZHRoIChjbSkiKSArDQogIHRoZW1lX2J3KCkgKyAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIEJsYWNrIGFuZCB3aGl0ZSB0aGVtZQ0KICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpICAgICAgICAgIyBIaWRlIGxlZ2VuZCAocmVkdW5kYW50IHdpdGggZmFjZXRzKQ0KYGBgDQoNCiMjIDIuNCBIaXN0b2dyYW1zIGFuZCBEZW5zaXR5IFBsb3RzIHdpdGggZ2dwbG90DQoNCmBgYHtyIGdncGxvdC1oaXN0LWRlbnNpdHksIGZpZy5oZWlnaHQ9Nn0NCiMgQ3JlYXRlIGNvbXBhcmlzb24gcGxvdA0KcDEgPC0gZ2dwbG90KGZhaXRoZnVsLCBhZXMoeCA9IHdhaXRpbmcpKSArDQogIGdlb21faGlzdG9ncmFtKGJpbndpZHRoID0gNSwgICAgICAgICAgICAjIGJpbndpZHRoOiB3aWR0aCBvZiBiaW5zDQogICAgICAgICAgICAgICAgIGZpbGwgPSAibGlnaHRibHVlIiwgICAgICAjIGZpbGw6IGludGVyaW9yIGNvbG9yDQogICAgICAgICAgICAgICAgIGNvbG9yID0gImJsYWNrIiwgICAgICAgICAjIGNvbG9yOiBib3JkZXIgY29sb3INCiAgICAgICAgICAgICAgICAgYWxwaGEgPSAwLjcpICsgICAgICAgICAgICMgYWxwaGE6IHRyYW5zcGFyZW5jeQ0KICBsYWJzKHRpdGxlID0gIkhpc3RvZ3JhbSIsIA0KICAgICAgIHggPSAiV2FpdGluZyBUaW1lIChtaW4pIiwgDQogICAgICAgeSA9ICJDb3VudCIpICsNCiAgdGhlbWVfbWluaW1hbCgpDQoNCnAyIDwtIGdncGxvdChmYWl0aGZ1bCwgYWVzKHggPSB3YWl0aW5nKSkgKw0KICBnZW9tX2RlbnNpdHkoZmlsbCA9ICJsaWdodGdyZWVuIiwgICAgICAgIyBEZW5zaXR5IHBsb3QgZmlsbA0KICAgICAgICAgICAgICAgYWxwaGEgPSAwLjUsIA0KICAgICAgICAgICAgICAgY29sb3IgPSAiZGFya2dyZWVuIikgKw0KICBsYWJzKHRpdGxlID0gIkRlbnNpdHkgUGxvdCIsDQogICAgICAgeCA9ICJXYWl0aW5nIFRpbWUgKG1pbikiLCANCiAgICAgICB5ID0gIkRlbnNpdHkiKSArDQogIHRoZW1lX21pbmltYWwoKQ0KDQpwMyA8LSBnZ3Bsb3QoZmFpdGhmdWwsIGFlcyh4ID0gd2FpdGluZykpICsNCiAgZ2VvbV9oaXN0b2dyYW0oYWVzKHkgPSAuLmRlbnNpdHkuLiksICAgICMgLi5kZW5zaXR5Li46IHVzZSBkZW5zaXR5IGluc3RlYWQgb2YgY291bnQNCiAgICAgICAgICAgICAgICAgYmlud2lkdGggPSA1LA0KICAgICAgICAgICAgICAgICBmaWxsID0gImxpZ2h0Y29yYWwiLA0KICAgICAgICAgICAgICAgICBhbHBoYSA9IDAuNSkgKw0KICBnZW9tX2RlbnNpdHkoY29sb3IgPSAiZGFya3JlZCIsIA0KICAgICAgICAgICAgICAgc2l6ZSA9IDEpICsgICAgICAgICAgICAgICAgIyBzaXplOiBsaW5lIHRoaWNrbmVzcw0KICBsYWJzKHRpdGxlID0gIkhpc3RvZ3JhbSArIERlbnNpdHkiLA0KICAgICAgIHggPSAiV2FpdGluZyBUaW1lIChtaW4pIiwgDQogICAgICAgeSA9ICJEZW5zaXR5IikgKw0KICB0aGVtZV9taW5pbWFsKCkNCg0KIyBBcnJhbmdlIHBsb3RzIHVzaW5nIHBhdGNod29yayAoaW5zdGFsbCBpZiBuZWVkZWQ6IGluc3RhbGwucGFja2FnZXMoInBhdGNod29yayIpKQ0KaWYocmVxdWlyZShwYXRjaHdvcmspKSB7DQogIHAxICsgcDIgKyBwMyArIHBsb3RfbGF5b3V0KG5jb2wgPSAzKQ0KfSBlbHNlIHsNCiAgcHJpbnQocDEpDQogIHByaW50KHAyKQ0KICBwcmludChwMykNCn0NCmBgYA0KDQojIyAyLjUgQm94IFBsb3RzIGFuZCBWaW9saW4gUGxvdHMNCg0KYGBge3IgZ2dwbG90LWJveC12aW9saW4sIGZpZy5oZWlnaHQ9Nn0NCiMgQ3JlYXRlIFRvb3RoR3Jvd3RoIHBsb3QNCmdncGxvdChUb290aEdyb3d0aCwgDQogICAgICAgYWVzKHggPSBmYWN0b3IoZG9zZSksICAgICAgICAgICMgZmFjdG9yKCk6IHRyZWF0IGRvc2UgYXMgY2F0ZWdvcmljYWwNCiAgICAgICAgICAgeSA9IGxlbiwgDQogICAgICAgICAgIGZpbGwgPSBmYWN0b3IoZG9zZSkpKSArICAgICMgZmlsbDogbWFwIGRvc2UgdG8gZmlsbCBjb2xvcg0KICBnZW9tX3Zpb2xpbihhbHBoYSA9IDAuNiwgICAgICAgICAgICAjIFZpb2xpbiBwbG90DQogICAgICAgICAgICAgIHRyaW0gPSBGQUxTRSkgKyAgICAgICAgICMgdHJpbTogZG9uJ3QgdHJpbSB0YWlscw0KICBnZW9tX2JveHBsb3Qod2lkdGggPSAwLjIsICAgICAgICAgICAjIEJveCBwbG90IGluc2lkZSB2aW9saW4NCiAgICAgICAgICAgICAgIGFscGhhID0gMC44KSArICAgIA0KICBnZW9tX2ppdHRlcih3aWR0aCA9IDAuMSwgICAgICAgICAgICAjIEppdHRlcmVkIHBvaW50cw0KICAgICAgICAgICAgICBzaXplID0gMS41LCANCiAgICAgICAgICAgICAgYWxwaGEgPSAwLjUpICsNCiAgbGFicyh0aXRsZSA9ICJUb290aCBHcm93dGggYnkgRG9zZSIsDQogICAgICAgc3VidGl0bGUgPSAiVmlvbGluICsgQm94ICsgSml0dGVyIFBsb3QiLA0KICAgICAgIHggPSAiRG9zZSAobWcvZGF5KSIsDQogICAgICAgeSA9ICJUb290aCBMZW5ndGgiLA0KICAgICAgIGZpbGwgPSAiRG9zZSIpICsNCiAgc2NhbGVfZmlsbF9icmV3ZXIocGFsZXR0ZSA9ICJTZXQyIikgKyAgIyBDb2xvckJyZXdlciBwYWxldHRlDQogIHRoZW1lX2NsYXNzaWMoKSAgICAgICAgICAgICAgICAgICAgICAjIENsYXNzaWMgdGhlbWUNCmBgYA0KDQojIyAyLjYgQmFyIFBsb3RzDQoNCmBgYHtyIGdncGxvdC1iYXIsIGZpZy5oZWlnaHQ9NX0NCiMgUHJlcGFyZSBUaXRhbmljIGRhdGENCnRpdGFuaWNfZGYgPC0gYXMuZGF0YS5mcmFtZShUaXRhbmljKQ0KcHJpbnQoaGVhZCh0aXRhbmljX2RmKSkNCg0KIyBCYXIgcGxvdA0KZ2dwbG90KHRpdGFuaWNfZGYsIA0KICAgICAgIGFlcyh4ID0gQ2xhc3MsICAgICAgICAgICAgICAgICAgIyB4OiBjYXRlZ29yaWNhbCB2YXJpYWJsZQ0KICAgICAgICAgICB5ID0gRnJlcSwgICAgICAgICAgICAgICAgICAgIyB5OiBmcmVxdWVuY3kNCiAgICAgICAgICAgZmlsbCA9IFN1cnZpdmVkKSkgKyAgICAgICAgICMgZmlsbDogY29sb3IgYnkgc3Vydml2YWwNCiAgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIsICAgICAgICAgICMgc3RhdDogdXNlIGFjdHVhbCB5IHZhbHVlcw0KICAgICAgICAgICBwb3NpdGlvbiA9ICJkb2RnZSIsICAgICAgICAgIyBwb3NpdGlvbjogYmFycyBzaWRlIGJ5IHNpZGUNCiAgICAgICAgICAgd2lkdGggPSAwLjcpICsgICAgICAgICAgICAgICMgd2lkdGg6IGJhciB3aWR0aCAoMC0xKQ0KICANCiAgbGFicyh0aXRsZSA9ICJUaXRhbmljIFN1cnZpdmFsIGJ5IENsYXNzIiwNCiAgICAgICB4ID0gIlBhc3NlbmdlciBDbGFzcyIsDQogICAgICAgeSA9ICJDb3VudCIsDQogICAgICAgZmlsbCA9ICJTdXJ2aXZlZCIpICsNCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gYygiWWVzIiA9ICIjNGRhZjRhIiwgIk5vIiA9ICIjZTQxYTFjIikpICsgICMgQ3VzdG9tIGNvbG9ycw0KICB0aGVtZV9taW5pbWFsKCkgKw0KICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDQ1LCBoanVzdCA9IDEpKSAgIyBSb3RhdGUgeCBsYWJlbHMNCmBgYA0KDQojIyAyLjcgSGVhdG1hcHMNCg0KYGBge3IgZ2dwbG90LWhlYXRtYXBfZGF0YSwgZmlnLmhlaWdodD02fQ0KIyBQcmVwYXJlIHZvbGNhbm8gZGF0YQ0KcHJpbnQoaGVhZCh2b2xjYW5vKSkNCnZvbGNhbm9fbG9uZyA8LSByZXNoYXBlMjo6bWVsdCh2b2xjYW5vKQ0KcHJpbnQoaGVhZCh2b2xjYW5vX2xvbmcpKQ0KYGBgDQpgYGB7ciBjb2xvcnMsIGZpZy5oZWlnaHQ9NiwgcmVzdWx0cz0naGlkZSd9DQojIENvbG9ycyBpbiBSDQpwYXIobWZyb3c9YygxLDUpKTsgeiA8LSBtYXRyaXgodm9sY2FubywgbnJvdyh2b2xjYW5vKSk7IA0KbGFwcGx5KGxpc3QodGVycmFpbi5jb2xvcnMsIA0KICAgICAgICAgICAgdG9wby5jb2xvcnMsIA0KICAgICAgICAgICAgaGVhdC5jb2xvcnMsIA0KICAgICAgICAgICAgY20uY29sb3JzLCANCiAgICAgICAgICAgIHJhaW5ib3cpLCBcKGYpIGltYWdlKHosIGNvbD1mKDEwMCksIGF4ZXM9RkFMU0UpKQ0KDQpgYGANCmBgYHtyIGdncGxvdC1oZWF0bWFwLCBmaWcuaGVpZ2h0PTZ9DQojIEhlYXRtYXAgd2l0aCBnZ3Bsb3QNCmdncGxvdCh2b2xjYW5vX2xvbmcsIA0KICAgICAgIGFlcyh4ID0gVmFyMSwgICAgICAgICAgICAgICAgICAgIyB4IGNvb3JkaW5hdGUNCiAgICAgICAgICAgeSA9IFZhcjIsICAgICAgICAgICAgICAgICAgICMgeSBjb29yZGluYXRlDQogICAgICAgICAgIGZpbGwgPSB2YWx1ZSkpICsgICAgICAgICAgICAjIGZpbGw6IGNvbG9yIGJ5IHZhbHVlDQogIGdlb21fdGlsZSgpICsgICAgICAgICAgICAgICAgICAgICAgICAjIGdlb21fdGlsZTogY3JlYXRlcyBoZWF0bWFwDQogIHNjYWxlX2ZpbGxfZ3JhZGllbnRuKGNvbG9ycyA9IHRlcnJhaW4uY29sb3JzKDEwKSwgICMgQ29sb3IgZ3JhZGllbnQNCiAgICAgICAgICAgICAgICAgICAgICAgbmFtZSA9ICJIZWlnaHQiKSArICAgICAgICAgICAgIyBMZWdlbmQgdGl0bGUNCiAgbGFicyh0aXRsZSA9ICJWb2xjYW5vIFRvcG9ncmFwaHkgSGVhdG1hcCIsDQogICAgICAgeCA9ICJYIENvb3JkaW5hdGUiLA0KICAgICAgIHkgPSAiWSBDb29yZGluYXRlIikgKw0KICB0aGVtZV9taW5pbWFsKCkgKw0KICB0aGVtZShwYW5lbC5ncmlkID0gZWxlbWVudF9ibGFuaygpKSAgIyBSZW1vdmUgZ3JpZCBsaW5lcw0KYGBgDQoNCiMjIDIuOCBDdXN0b20gVGhlbWVzIGFuZCBTYXZpbmcgUGxvdHMNCg0KYGBge3IgZ2dwbG90LWN1c3RvbS10aGVtZSwgZmlnLmhlaWdodD01fQ0KIyBDcmVhdGUgY3VzdG9tIHBsb3QNCmN1c3RvbV9wbG90IDwtIGdncGxvdChpcmlzLCBhZXMoeCA9IFNwZWNpZXMsIHkgPSBTZXBhbC5MZW5ndGgsIGZpbGwgPSBTcGVjaWVzKSkgKw0KICBnZW9tX2JveHBsb3QoYWxwaGEgPSAwLjgsDQogICAgICAgICAgICAgICBvdXRsaWVyLmNvbG9yID0gInJlZCIsDQogICAgICAgICAgICAgICBvdXRsaWVyLnNpemUgPSAyKSArDQogIGdlb21faml0dGVyKHdpZHRoID0gMC4yLCANCiAgICAgICAgICAgICAgc2l6ZSA9IDEuNSwgDQogICAgICAgICAgICAgIGFscGhhID0gMC40KSArDQogIGxhYnModGl0bGUgPSAiU2VwYWwgTGVuZ3RoIGJ5IFNwZWNpZXMiLA0KICAgICAgIHN1YnRpdGxlID0gIklyaXMgRGF0YXNldCBBbmFseXNpcyIsDQogICAgICAgeCA9ICJTcGVjaWVzIiwNCiAgICAgICB5ID0gIlNlcGFsIExlbmd0aCAoY20pIiwNCiAgICAgICBjYXB0aW9uID0gIlNvdXJjZTogRmlzaGVyJ3MgSXJpcyBEYXRhc2V0IikgKw0KICBzY2FsZV9maWxsX2JyZXdlcihwYWxldHRlID0gIlNldDMiKSArDQogIA0KICAjIEN1c3RvbSB0aGVtZQ0KICB0aGVtZSgNCiAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNiwgICAgICAgICAgICMgVGl0bGUgc2l6ZQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmFjZSA9ICJib2xkIiwgICAgICAgIyBGb250IGZhY2UNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGhqdXN0ID0gMC41KSwgICAgICAgIyBIb3Jpem9udGFsIGp1c3RpZmljYXRpb24NCiAgICBwbG90LnN1YnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGhqdXN0ID0gMC41KSwNCiAgICBheGlzLnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMiwgICAgICAgICAgICMgQXhpcyB0aXRsZSBzaXplDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmYWNlID0gImJvbGQiKSwNCiAgICBheGlzLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwKSwgICAgICAgICAgIyBBeGlzIHRleHQgc2l6ZQ0KICAgIGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfdGV4dChmYWNlID0gImJvbGQiKSwNCiAgICBsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIiwgICAgICAgICAgICAgICAgICAgIyBMZWdlbmQgcG9zaXRpb24NCiAgICBwYW5lbC5iYWNrZ3JvdW5kID0gZWxlbWVudF9yZWN0KGZpbGwgPSAid2hpdGUiKSwgICMgUGFuZWwgYmFja2dyb3VuZA0KICAgIHBhbmVsLmdyaWQubWFqb3IgPSBlbGVtZW50X2xpbmUoY29sb3IgPSAiZ3JheTkwIiwgICMgTWFqb3IgZ3JpZCBsaW5lcw0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2l6ZSA9IDAuNSksDQogICAgcGFuZWwuZ3JpZC5taW5vciA9IGVsZW1lbnRfYmxhbmsoKSwgICAgICAgICAgICMgUmVtb3ZlIG1pbm9yIGdyaWQNCiAgICBwbG90LmJhY2tncm91bmQgPSBlbGVtZW50X3JlY3QoZmlsbCA9ICJ3aGl0ZSIsICAjIFBsb3QgYmFja2dyb3VuZA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2xvciA9ICJibGFjayIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNpemUgPSAxKQ0KICApDQoNCnByaW50KGN1c3RvbV9wbG90KQ0KDQojIFNhdmUgcGxvdA0KZ2dzYXZlKCJmaWd1cmVzL2N1c3RvbV9nZ3Bsb3QucG5nIiwgICAgICAgICMgZmlsZW5hbWUNCiAgICAgICBwbG90ID0gY3VzdG9tX3Bsb3QsICAgICAgICAgICAgICAgICAjIHBsb3QgdG8gc2F2ZQ0KICAgICAgIHdpZHRoID0gMTAsICAgICAgICAgICAgICAgICAgICAgICAgICMgd2lkdGggaW4gaW5jaGVzDQogICAgICAgaGVpZ2h0ID0gNiwgICAgICAgICAgICAgICAgICAgICAgICAgIyBoZWlnaHQgaW4gaW5jaGVzDQogICAgICAgZHBpID0gMzAwKSAgICAgICAgICAgICAgICAgICAgICAgICAgIyByZXNvbHV0aW9uDQpgYGANCg0KIyMgMi45IGdncGxvdDIgRXhlcmNpc2VzDQoNCmBgYHtyIGdncGxvdC1leGVyY2lzZXMsIGV2YWw9RkFMU0V9DQojIEVYRVJDSVNFIDQ6IENyZWF0ZSBhIGdncGxvdCBvZiBtdGNhcnMgc2hvd2luZzoNCiMgMS4gU2NhdHRlciBwbG90IG9mIG1wZyB2cyBocA0KIyAyLiBDb2xvciBwb2ludHMgYnkgdHJhbnNtaXNzaW9uIHR5cGUgKGFtKQ0KIyAzLiBBZGQgc21vb3RoIHRyZW5kIGxpbmUNCiMgNC4gRmFjZXQgYnkgbnVtYmVyIG9mIGN5bGluZGVycw0KIyA1LiBBcHBseSB0aGVtZV9jbGFzc2ljKCkNCg0KIyBFWEVSQ0lTRSA1OiBDcmVhdGUgYSBiYXIgcGxvdCBvZiBkaWFtb25kIGNvdW50cyBieSBjdXQNCiMgMS4gVXNlIGRpYW1vbmRzIGRhdGFzZXQgKGdncGxvdDI6OmRpYW1vbmRzKQ0KIyAyLiBGaWxsIGJhcnMgYnkgY29sb3INCiMgMy4gVXNlIHBvc2l0aW9uID0gImZpbGwiIGZvciBwcm9wb3J0aW9ucw0KIyA0LiBBZGQgcGVyY2VudGFnZSBsYWJlbHMNCg0KIyBFWEVSQ0lTRSA2OiBDcmVhdGUgYSBsaW5lIHBsb3Qgb2YgZWNvbm9taWNzIGRhdGFzZXQNCiMgMS4gUGxvdCB1bmVtcGxveW1lbnQgcmF0ZSBvdmVyIHRpbWUNCiMgMi4gQWRkIHZlcnRpY2FsIGxpbmUgZm9yIHNpZ25pZmljYW50IGV2ZW50cw0KIyAzLiBBZGQgc2hhZGVkIHJlZ2lvbiBmb3IgcmVjZXNzaW9uIHBlcmlvZHMNCiMgNC4gVXNlIHNjYWxlX3hfZGF0ZSgpIGZvciBwcm9wZXIgZGF0ZSBmb3JtYXR0aW5nDQpgYGANCg0KLS0tDQoNCiMgU2VjdGlvbiAzOiBQTE9UTFkgSU5URVJBQ1RJVkUgVklTVUFMSVpBVElPTg0KDQojIyAzLjEgSW50cm9kdWN0aW9uIHRvIFBsb3RseQ0KDQpgYGB7ciBwbG90bHktaW50cm99DQojIEJhc2ljIHBsb3RseSBzeW50YXgNCmNhdCgNCiAgInBsb3RseSBCYXNpYyBTeW50YXg6XG4iLA0KICAicGxvdF9seShkYXRhLCB4ID0gfnZhcjEsIHkgPSB+dmFyMiwgdHlwZSA9ICdzY2F0dGVyJywgbW9kZSA9ICdtYXJrZXJzJylcbiIsDQogICJcbkNvbW1vbiB0eXBlIHZhbHVlczpcbiIsDQogICItICdzY2F0dGVyJzogc2NhdHRlci9saW5lIHBsb3RzXG4iLA0KICAiLSAnYmFyJzogYmFyIGNoYXJ0c1xuIiwNCiAgIi0gJ2hpc3RvZ3JhbSc6IGhpc3RvZ3JhbXNcbiIsDQogICItICdib3gnOiBib3ggcGxvdHNcbiIsDQogICItICdoZWF0bWFwJzogaGVhdG1hcHNcbiIsDQogIHNlcCA9ICIiDQopDQoNCmBgYA0KDQojIyAzLjIgQmFzaWMgSW50ZXJhY3RpdmUgU2NhdHRlciBQbG90DQoNCmBgYHtyIHBsb3RseS1iYXNpYywgZmlnLmhlaWdodD01fQ0KIyBCYXNpYyBpbnRlcmFjdGl2ZSBzY2F0dGVyIHBsb3QNCnAgPC0gcGxvdF9seShtdGNhcnMsICAgICAgICAgICAgICAgICAgICAgICMgZGF0YTogZGF0YXNldA0KICAgICAgICAgICAgIHggPSB+d3QsICAgICAgICAgICAgICAgICAgICAgIyB4OiB3ZWlnaHQgKH4gbWVhbnMgZm9ybXVsYSkNCiAgICAgICAgICAgICB5ID0gfm1wZywgICAgICAgICAgICAgICAgICAgICMgeTogbXBnDQogICAgICAgICAgICAgdHlwZSA9ICdzY2F0dGVyJywgICAgICAgICAgICAjIHR5cGU6IHBsb3QgdHlwZQ0KICAgICAgICAgICAgIG1vZGUgPSAnbWFya2VycycsICAgICAgICAgICAgIyBtb2RlOiBkaXNwbGF5IG1vZGUNCiAgICAgICAgICAgICBtYXJrZXIgPSBsaXN0KHNpemUgPSAxMCwgICAgICMgbWFya2VyOiBwb2ludCBwcm9wZXJ0aWVzDQogICAgICAgICAgICAgICAgICAgICAgICAgICBjb2xvciA9ICdyZ2JhKDMwLCAxMjAsIDE4MCwgMC44KScsICAjIFJHQkEgY29sb3INCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGxpbmUgPSBsaXN0KGNvbG9yID0gJ3JnYigwLDAsMCknLCAgICMgQm9yZGVyIGNvbG9yDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB3aWR0aCA9IDEpKSwgICAgICAgICAgICAjIEJvcmRlciB3aWR0aA0KICAgICAgICAgICAgIHRleHQgPSB+cGFzdGUoJ0NhcjonLCByb3duYW1lcyhtdGNhcnMpLCAgIyB0ZXh0OiBob3ZlciB0ZXh0DQogICAgICAgICAgICAgICAgICAgICAgICAgICAnPGJyPk1QRzonLCBtcGcsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAnPGJyPldlaWdodDonLCB3dCksDQogICAgICAgICAgICAgaG92ZXJpbmZvID0gJ3RleHQnKSAlPiUgICAgICAjIGhvdmVyaW5mbzogd2hhdCB0byBzaG93IG9uIGhvdmVyDQogIGxheW91dCh0aXRsZSA9ICdJbnRlcmFjdGl2ZSBTY2F0dGVyIFBsb3QnLCAgIyBsYXlvdXQ6IHBsb3QgbGF5b3V0DQogICAgICAgICB4YXhpcyA9IGxpc3QodGl0bGUgPSAnV2VpZ2h0ICgxMDAwIGxicyknKSwgICMgeGF4aXMgcHJvcGVydGllcw0KICAgICAgICAgeWF4aXMgPSBsaXN0KHRpdGxlID0gJ01pbGVzIHBlciBHYWxsb24nKSwgICAjIHlheGlzIHByb3BlcnRpZXMNCiAgICAgICAgIGhvdmVybW9kZSA9ICdjbG9zZXN0JykgICAgICAgICAgICMgaG92ZXJtb2RlOiBob3cgaG92ZXIgd29ya3MNCg0KcA0KYGBgDQoNCiMjIDMuMyBDb2xvcmVkIFNjYXR0ZXIgd2l0aCBMZWdlbmQNCg0KYGBge3IgcGxvdGx5LWNvbG9yZWQsIGZpZy5oZWlnaHQ9NX0NCiMgQ29sb3JlZCBzY2F0dGVyIHBsb3Qgd2l0aCBncm91cHMNCnAgPC0gcGxvdF9seShtdGNhcnMsDQogICAgICAgICAgICAgeCA9IH53dCwNCiAgICAgICAgICAgICB5ID0gfm1wZywNCiAgICAgICAgICAgICBjb2xvciA9IH5mYWN0b3IoY3lsKSwgICAgICAgICMgY29sb3I6IG1hcCB0byBjb2xvciBzY2FsZQ0KICAgICAgICAgICAgIGNvbG9ycyA9IGMoJyNlNDFhMWMnLCAnIzM3N2ViOCcsICcjNGRhZjRhJyksICAjIEN1c3RvbSBjb2xvcnMNCiAgICAgICAgICAgICB0eXBlID0gJ3NjYXR0ZXInLA0KICAgICAgICAgICAgIG1vZGUgPSAnbWFya2VycycsDQogICAgICAgICAgICAgc2l6ZSA9IH5ocCwgICAgICAgICAgICAgICAgICAjIHNpemU6IG1hcCB0byBwb2ludCBzaXplDQogICAgICAgICAgICAgc2l6ZXMgPSBjKDUsIDIwKSwgICAgICAgICAgICAjIHNpemVzOiBtaW4gYW5kIG1heCBzaXplDQogICAgICAgICAgICAgbWFya2VyID0gbGlzdChvcGFjaXR5ID0gMC43LCAjIG9wYWNpdHk6IHRyYW5zcGFyZW5jeQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgc2l6ZW1vZGUgPSAnZGlhbWV0ZXInKSwgICMgc2l6ZW1vZGU6IGhvdyB0byBpbnRlcnByZXQgc2l6ZQ0KICAgICAgICAgICAgIHRleHQgPSB+cGFzdGUoJ0NhcjonLCByb3duYW1lcyhtdGNhcnMpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgJzxicj5DeWxpbmRlcnM6JywgY3lsLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgJzxicj5IUDonLCBocCksDQogICAgICAgICAgICAgaG92ZXJpbmZvID0gJ3RleHQnKSAlPiUNCiAgbGF5b3V0KHRpdGxlID0gJ1dlaWdodCB2cyBNUEcgKEludGVyYWN0aXZlKScsDQogICAgICAgICB4YXhpcyA9IGxpc3QodGl0bGUgPSAnV2VpZ2h0JyksDQogICAgICAgICB5YXhpcyA9IGxpc3QodGl0bGUgPSAnTVBHJyksDQogICAgICAgICBsZWdlbmQgPSBsaXN0KHRpdGxlID0gbGlzdCh0ZXh0ID0gJ0N5bGluZGVycycpKSwgICMgTGVnZW5kIHRpdGxlDQogICAgICAgICBob3Zlcm1vZGUgPSAnY2xvc2VzdCcpDQoNCnANCmBgYA0KDQojIyAzLjQgM0QgU2NhdHRlciBQbG90DQoNCmBgYHtyIHBsb3RseS0zZCwgZmlnLmhlaWdodD02fQ0KIyBJbnRlcmFjdGl2ZSAzRCBzY2F0dGVyIHBsb3QNCnAgPC0gcGxvdF9seShtdGNhcnMsDQogICAgICAgICAgICAgeCA9IH53dCwNCiAgICAgICAgICAgICB5ID0gfm1wZywNCiAgICAgICAgICAgICB6ID0gfmhwLCAgICAgICAgICAgICAgICAgICAgICMgejogdGhpcmQgZGltZW5zaW9uDQogICAgICAgICAgICAgY29sb3IgPSB+ZmFjdG9yKGN5bCksDQogICAgICAgICAgICAgY29sb3JzID0gYygnI2ZmN2YwMCcsICcjOTg0ZWEzJywgJyNmZmZmMzMnKSwNCiAgICAgICAgICAgICB0eXBlID0gJ3NjYXR0ZXIzZCcsICAgICAgICAgICMgdHlwZTogM0Qgc2NhdHRlcg0KICAgICAgICAgICAgIG1vZGUgPSAnbWFya2VycycsDQogICAgICAgICAgICAgbWFya2VyID0gbGlzdChzaXplID0gNSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgIG9wYWNpdHkgPSAwLjgpLA0KICAgICAgICAgICAgIHRleHQgPSB+cm93bmFtZXMobXRjYXJzKSkgJT4lDQogIGxheW91dCh0aXRsZSA9ICczRCBTY2F0dGVyIFBsb3QnLA0KICAgICAgICAgc2NlbmUgPSBsaXN0KCAgICAgICAgICAgICAgICAgICAgIyBzY2VuZTogM0Qgc2NlbmUgcHJvcGVydGllcw0KICAgICAgICAgICB4YXhpcyA9IGxpc3QodGl0bGUgPSAnV2VpZ2h0JyksDQogICAgICAgICAgIHlheGlzID0gbGlzdCh0aXRsZSA9ICdNUEcnKSwNCiAgICAgICAgICAgemF4aXMgPSBsaXN0KHRpdGxlID0gJ0hvcnNlcG93ZXInKSwNCiAgICAgICAgICAgY2FtZXJhID0gbGlzdCggICAgICAgICAgICAgICAgICMgY2FtZXJhOiB2aWV3aW5nIGFuZ2xlDQogICAgICAgICAgICAgZXllID0gbGlzdCh4ID0gMS41LCB5ID0gMS41LCB6ID0gMS41KSAgIyBleWUgcG9zaXRpb24NCiAgICAgICAgICAgKQ0KICAgICAgICAgKSkNCg0KcA0KYGBgDQoNCiMjIDMuNSBJbnRlcmFjdGl2ZSBUaW1lIFNlcmllcw0KDQpgYGB7ciBwbG90bHktdGltZXNlcmllcywgZmlnLmhlaWdodD01fQ0KIyBDcmVhdGUgdGltZSBzZXJpZXMgZGF0YQ0KZGF0ZXMgPC0gc2VxLkRhdGUoZnJvbSA9IGFzLkRhdGUoJzIwMjAtMDEtMDEnKSwgDQogICAgICAgICAgICAgICAgICBieSA9ICdtb250aCcsIA0KICAgICAgICAgICAgICAgICAgbGVuZ3RoLm91dCA9IDI0KQ0Kc2FsZXMgPC0gY3Vtc3VtKHJub3JtKDI0LCBtZWFuID0gMTAwLCBzZCA9IDIwKSkNCg0KIyBJbnRlcmFjdGl2ZSBsaW5lIHBsb3QNCnAgPC0gcGxvdF9seSh4ID0gZGF0ZXMsICAgICAgICAgICAgICAgICAgICMgeDogZGF0ZXMNCiAgICAgICAgICAgICB5ID0gc2FsZXMsICAgICAgICAgICAgICAgICAgICAjIHk6IHNhbGVzDQogICAgICAgICAgICAgdHlwZSA9ICdzY2F0dGVyJywNCiAgICAgICAgICAgICBtb2RlID0gJ2xpbmVzK21hcmtlcnMnLCAgICAgICMgbW9kZTogbGluZXMgYW5kIG1hcmtlcnMNCiAgICAgICAgICAgICBsaW5lID0gbGlzdChjb2xvciA9ICdyZ2IoMzEsIDExOSwgMTgwKScsICAjIGxpbmUgcHJvcGVydGllcw0KICAgICAgICAgICAgICAgICAgICAgICAgIHdpZHRoID0gMiwNCiAgICAgICAgICAgICAgICAgICAgICAgICBkYXNoID0gJ3NvbGlkJyksDQogICAgICAgICAgICAgbWFya2VyID0gbGlzdChzaXplID0gOCwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbG9yID0gJ3JnYigyNTUsIDEyNywgMTQpJyksDQogICAgICAgICAgICAgbmFtZSA9ICdTYWxlcycpICU+JSAgICAgICAgICAjIG5hbWU6IHRyYWNlIG5hbWUNCiAgbGF5b3V0KHRpdGxlID0gJ1NhbGVzIE92ZXIgVGltZScsDQogICAgICAgICB4YXhpcyA9IGxpc3QodGl0bGUgPSAnRGF0ZScsDQogICAgICAgICAgICAgICAgICAgICAgdHlwZSA9ICdkYXRlJywgICAgICAjIHR5cGU6IGRhdGUgYXhpcw0KICAgICAgICAgICAgICAgICAgICAgIHRpY2tmb3JtYXQgPSAnJWIgJVknKSwgICMgRGF0ZSBmb3JtYXQNCiAgICAgICAgIHlheGlzID0gbGlzdCh0aXRsZSA9ICdTYWxlcyAoJCknKSwNCiAgICAgICAgIGhvdmVybW9kZSA9ICd4IHVuaWZpZWQnKSAgICAgICAgICMgU2hvdyBhbGwgeSB2YWx1ZXMgYXQgeCBwb3NpdGlvbg0KDQpwDQpgYGANCg0KIyMgMy42IEludGVyYWN0aXZlIEJhciBQbG90DQoNCmBgYHtyIHBsb3RseS1iYXItaW50ZXJhY3RpdmUsIGZpZy5oZWlnaHQ9NX0NCiMgUHJlcGFyZSBUaXRhbmljIGRhdGEgZm9yIHBsb3RseQ0KdGl0YW5pY19zdW1tYXJ5IDwtIGFnZ3JlZ2F0ZShGcmVxIH4gQ2xhc3MgKyBTdXJ2aXZlZCwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgZGF0YSA9IHRpdGFuaWNfZGYsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN1bSkNCg0KIyBJbnRlcmFjdGl2ZSBiYXIgcGxvdA0KcCA8LSBwbG90X2x5KHRpdGFuaWNfc3VtbWFyeSwNCiAgICAgICAgICAgICB4ID0gfkNsYXNzLA0KICAgICAgICAgICAgIHkgPSB+RnJlcSwNCiAgICAgICAgICAgICBjb2xvciA9IH5TdXJ2aXZlZCwNCiAgICAgICAgICAgICBjb2xvcnMgPSBjKCcjZDcxOTFjJywgJyMyYzdiYjYnKSwNCiAgICAgICAgICAgICB0eXBlID0gJ2JhcicsDQogICAgICAgICAgICAgdGV4dCA9IH5GcmVxLA0KICAgICAgICAgICAgIHRleHRwb3NpdGlvbiA9ICdhdXRvJywgICAgICAgIyB0ZXh0cG9zaXRpb246IGF1dG8gcG9zaXRpb24gdGV4dA0KICAgICAgICAgICAgIGhvdmVydGV4dCA9IH5wYXN0ZSgnQ2xhc3M6JywgQ2xhc3MsICAjIGhvdmVydGV4dDogY3VzdG9tIGhvdmVyDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJzxicj5TdXJ2aXZlZDonLCBTdXJ2aXZlZCwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAnPGJyPkNvdW50OicsIEZyZXEpLA0KICAgICAgICAgICAgIGhvdmVyaW5mbyA9ICd0ZXh0JykgJT4lDQogIGxheW91dCh0aXRsZSA9ICdUaXRhbmljIFN1cnZpdmFsIGJ5IENsYXNzJywNCiAgICAgICAgIHhheGlzID0gbGlzdCh0aXRsZSA9ICdQYXNzZW5nZXIgQ2xhc3MnKSwNCiAgICAgICAgIHlheGlzID0gbGlzdCh0aXRsZSA9ICdDb3VudCcpLA0KICAgICAgICAgYmFybW9kZSA9ICdncm91cCcsICAgICAgICAgICAgICAgIyBiYXJtb2RlOiBncm91cGVkIGJhcnMNCiAgICAgICAgIGJhcmdhcCA9IDAuMTUsICAgICAgICAgICAgICAgICAgICMgYmFyZ2FwOiBnYXAgYmV0d2VlbiBiYXJzDQogICAgICAgICBiYXJncm91cGdhcCA9IDAuMSwgICAgICAgICAgICAgICAjIGJhcmdyb3VwZ2FwOiBnYXAgYmV0d2VlbiBncm91cHMNCiAgICAgICAgIGhvdmVybGFiZWwgPSBsaXN0KG5hbWVsZW5ndGggPSAtMSkpICAjIFNob3cgZnVsbCBob3ZlciBsYWJlbA0KDQpwDQpgYGANCg0KIyMgMy43IEludGVyYWN0aXZlIEhpc3RvZ3JhbQ0KDQpgYGB7ciBwbG90bHktaGlzdG9ncmFtLWludGVyYWN0aXZlLCBmaWcuaGVpZ2h0PTV9DQojIEludGVyYWN0aXZlIGhpc3RvZ3JhbQ0KcCA8LSBwbG90X2x5KHggPSBmYWl0aGZ1bCR3YWl0aW5nLA0KICAgICAgICAgICAgIHR5cGUgPSAnaGlzdG9ncmFtJywNCiAgICAgICAgICAgICBuYmluc3ggPSAyMCwgICAgICAgICAgICAgICAgICMgbmJpbnN4OiBudW1iZXIgb2YgYmlucw0KICAgICAgICAgICAgIG1hcmtlciA9IGxpc3QoY29sb3IgPSAncmdiKDE1OCwyMDIsMjI1KScsICAjIEJhciBjb2xvcg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgbGluZSA9IGxpc3QoY29sb3IgPSAncmdiKDgsNDgsMTA3KScsICAjIEJvcmRlciBjb2xvcg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgd2lkdGggPSAxLjUpKSwNCiAgICAgICAgICAgICBvcGFjaXR5ID0gMC43LCAgICAgICAgICAgICAgICMgb3BhY2l0eTogdHJhbnNwYXJlbmN5DQogICAgICAgICAgICAgbmFtZSA9ICdXYWl0aW5nIFRpbWUnKSAlPiUgICAjIG5hbWU6IGxlZ2VuZCBuYW1lDQogIGxheW91dCh0aXRsZSA9ICdXYWl0aW5nIFRpbWUgRGlzdHJpYnV0aW9uJywNCiAgICAgICAgIHhheGlzID0gbGlzdCh0aXRsZSA9ICdXYWl0aW5nIFRpbWUgKG1pbnV0ZXMpJywNCiAgICAgICAgICAgICAgICAgICAgICByYW5nZSA9IGMoNDAsIDEwMCkpLCAgIyByYW5nZTogYXhpcyByYW5nZQ0KICAgICAgICAgeWF4aXMgPSBsaXN0KHRpdGxlID0gJ0NvdW50JyksDQogICAgICAgICBiYXJnYXAgPSAwLjA1LCAgICAgICAgICAgICAgICAgICAjIGJhcmdhcDogZ2FwIGJldHdlZW4gYmFycw0KICAgICAgICAgaG92ZXJtb2RlID0gJ3gnKSAgICAgICAgICAgICAgICAgIyBTaG93IGhpc3RvZ3JhbSBiaW4gaW5mbw0KDQpwDQpgYGANCg0KIyMgMy44IFBsb3RseSBFeGVyY2lzZXMNCg0KYGBge3IgcGxvdGx5LWV4ZXJjaXNlcywgZXZhbD1GQUxTRX0NCiMgRVhFUkNJU0UgNzogQ3JlYXRlIGFuIGludGVyYWN0aXZlIHBsb3Qgb2YgcXVha2VzIGRhdGFzZXQNCiMgMS4gMkQgc2NhdHRlciBvZiBsYXQgdnMgbG9uZw0KIyAyLiBDb2xvciBieSBkZXB0aA0KIyAzLiBTaXplIGJ5IG1hZ25pdHVkZQ0KIyA0LiBBZGQgaG92ZXIgaW5mb3JtYXRpb24gd2l0aCBhbGwgdmFyaWFibGVzDQoNCiMgRVhFUkNJU0UgODogQ3JlYXRlIGludGVyYWN0aXZlIHZvbGNhbm8gc3VyZmFjZSBwbG90DQojIDEuIFVzZSBwbG90X2x5IHR5cGUgPSAic3VyZmFjZSINCiMgMi4gQWRkIGNvbnRvdXJzDQojIDMuIEN1c3RvbWl6ZSBjb2xvcnNjYWxlDQojIDQuIEFkZCBsaWdodGluZyBlZmZlY3RzDQoNCiMgRVhFUkNJU0UgOTogQ3JlYXRlIGRhc2hib2FyZCB3aXRoIHN1YnBsb3RzDQojIDEuIENvbWJpbmUgc2NhdHRlciwgaGlzdG9ncmFtLCBhbmQgYm94IHBsb3QNCiMgMi4gTGluayBzZWxlY3Rpb25zIGJldHdlZW4gcGxvdHMNCiMgMy4gQWRkIGRyb3Bkb3duIG1lbnVzIGZvciB2YXJpYWJsZSBzZWxlY3Rpb24NCmBgYA0KDQotLS0NCg0KIyBTZWN0aW9uIDQ6IEJFU1QgUFJBQ1RJQ0VTIEFORCBDT01QQVJJU09ODQoNCiMjIDQuMSBXaGVuIHRvIFVzZSBFYWNoIFRvb2wNCg0KYGBge3IgY29tcGFyaXNvbi10YWJsZX0NCiMgQ3JlYXRlIGNvbXBhcmlzb24gZGF0YSBmcmFtZQ0KY29tcGFyaXNvbiA8LSBkYXRhLmZyYW1lKA0KICBGZWF0dXJlID0gYygiTGVhcm5pbmcgQ3VydmUiLCAiQ3VzdG9taXphdGlvbiIsICJJbnRlcmFjdGl2aXR5IiwgDQogICAgICAgICAgICAgICJQdWJsaWNhdGlvbiBRdWFsaXR5IiwgIlNwZWVkIiwgIkVhc2Ugb2YgVXNlIiksDQogIEJhc2VfUiA9IGMoIkVhc3kiLCAiQmFzaWMiLCAiTm9uZSIsICJCYXNpYyIsICJGYXN0IiwgIlZlcnkgRWFzeSIpLA0KICBnZ3Bsb3QyID0gYygiTW9kZXJhdGUiLCAiRXhjZWxsZW50IiwgIkxpbWl0ZWQiLCAiRXhjZWxsZW50IiwgIk1vZGVyYXRlIiwgIk1vZGVyYXRlIiksDQogIFBsb3RseSA9IGMoIlN0ZWVwIiwgIkdvb2QiLCAiRXhjZWxsZW50IiwgIkdvb2QiLCAiU2xvdyIsICJDb21wbGV4IikNCikNCg0KIyBEaXNwbGF5IGFzIGludGVyYWN0aXZlIHRhYmxlDQpEVDo6ZGF0YXRhYmxlKGNvbXBhcmlzb24sDQogICAgICAgICAgICAgIG9wdGlvbnMgPSBsaXN0KHBhZ2VMZW5ndGggPSA2LCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZG9tID0gJ3QnKSwNCiAgICAgICAgICAgICAgcm93bmFtZXMgPSBGQUxTRSkgJT4lDQogIERUOjpmb3JtYXRTdHlsZShjb2x1bW5zID0gMTo0LCANCiAgICAgICAgICAgICAgICAgIGZvbnRTaXplID0gJzEycHgnKQ0KYGBgDQoNCiMjIDQuMiBDb2xvciBQYWxldHRlIEJlc3QgUHJhY3RpY2VzDQoNCmBgYHtyIGNvbG9yLXBhbGV0dGVzLCBmaWcuaGVpZ2h0PTR9DQojIFNob3cgZGlmZmVyZW50IGNvbG9yIHBhbGV0dGVzDQpwYXIobWZyb3cgPSBjKDEsIDMpLCBtYXIgPSBjKDIsIDIsIDIsIDEpKQ0KDQojIFNlcXVlbnRpYWwgcGFsZXR0ZSAoZm9yIG9yZGVyZWQgZGF0YSkNCmltYWdlKHZvbGNhbm9bMToxMCwgMToxMF0sIA0KICAgICAgY29sID0gYnJld2VyLnBhbCg5LCAiQmx1ZXMiKSwNCiAgICAgIG1haW4gPSAiU2VxdWVudGlhbCAoQmx1ZXMpIikNCg0KIyBEaXZlcmdpbmcgcGFsZXR0ZSAoZm9yIGRhdGEgd2l0aCBtaWRwb2ludCkNCmltYWdlKHZvbGNhbm9bMToxMCwgMToxMF0gLSBtZWFuKHZvbGNhbm9bMToxMCwgMToxMF0pLCANCiAgICAgIGNvbCA9IGJyZXdlci5wYWwoMTEsICJSZEJ1IiksDQogICAgICBtYWluID0gIkRpdmVyZ2luZyAoUmRCdSkiKQ0KDQojIFF1YWxpdGF0aXZlIHBhbGV0dGUgKGZvciBjYXRlZ29yaWNhbCBkYXRhKQ0KYmFycGxvdChyZXAoMSwgOCksIA0KICAgICAgICBjb2wgPSBicmV3ZXIucGFsKDgsICJTZXQzIiksDQogICAgICAgIG1haW4gPSAiUXVhbGl0YXRpdmUgKFNldDMpIiwNCiAgICAgICAgYm9yZGVyID0gTkEpDQoNCnBhcihtZnJvdyA9IGMoMSwgMSkpDQpgYGANCg0KIyMgNC4zIEV4cG9ydGluZyBQbG90cw0KDQpgYGB7ciBleHBvcnQtZXhhbXBsZXMsIGV2YWw9RkFMU0V9DQojIEV4cG9ydCBCYXNlIFIgcGxvdA0KcG5nKCJmaWd1cmVzL2Jhc2Vfc2NhdHRlci5wbmciLCAgICAjIGZpbGVuYW1lDQogICAgd2lkdGggPSAyMDAwLCAgICAgICAgICAgICAgICAgICMgd2lkdGggaW4gcGl4ZWxzDQogICAgaGVpZ2h0ID0gMTUwMCwgICAgICAgICAgICAgICAgICMgaGVpZ2h0IGluIHBpeGVscw0KICAgIHJlcyA9IDMwMCkgICAgICAgICAgICAgICAgICAgICAjIHJlc29sdXRpb24gKERQSSkNCnBsb3QobXRjYXJzJHd0LCBtdGNhcnMkbXBnLA0KICAgICBtYWluID0gIkV4cG9ydGVkIFBsb3QiLA0KICAgICB4bGFiID0gIldlaWdodCIsDQogICAgIHlsYWIgPSAiTVBHIikNCmRldi5vZmYoKQ0KDQojIEV4cG9ydCBnZ3Bsb3QNCmdnc2F2ZSgiZmlndXJlcy9nZ3Bsb3RfZXhwb3J0LnBuZyIsDQogICAgICAgcGxvdCA9IGN1c3RvbV9wbG90LA0KICAgICAgIHdpZHRoID0gMTAsICAgICAjIGluY2hlcw0KICAgICAgIGhlaWdodCA9IDYsICAgICAjIGluY2hlcw0KICAgICAgIGRwaSA9IDMwMCkNCg0KIyBFeHBvcnQgcGxvdGx5IChhcyBIVE1MKQ0KaHRtbHdpZGdldHM6OnNhdmVXaWRnZXQocCwgImZpZ3VyZXMvcGxvdGx5X2V4cG9ydC5odG1sIikNCmBgYA0KDQojIyA0LjQgUGVyZm9ybWFuY2UgVGlwcw0KDQpgYGB7ciBwZXJmb3JtYW5jZS10aXBzfQ0KY2F0KCJQZXJmb3JtYW5jZSBPcHRpbWl6YXRpb24gVGlwczpcblxuIiwNCiAgICAi4oCiIEZvciBsYXJnZSBkYXRhc2V0cyAoPjEwayBwb2ludHMpOlxuIiwNCiAgICAiICAtIFVzZSBoZXhiaW4gcGxvdHMgb3IgMkQgZGVuc2l0eVxuIiwNCiAgICAiICAtIFNhbXBsZSBkYXRhIGZvciBpbml0aWFsIGV4cGxvcmF0aW9uXG4iLA0KICAgICIgIC0gQ29uc2lkZXIgZGF0YSBhZ2dyZWdhdGlvblxuXG4iLA0KICAgICLigKIgTWVtb3J5IG1hbmFnZW1lbnQ6XG4iLA0KICAgICIgIC0gUmVtb3ZlIHVudXNlZCBvYmplY3RzOiBybShvYmplY3QpXG4iLA0KICAgICIgIC0gQ2xlYXIgcGxvdHM6IGRldi5vZmYoKVxuIiwNCiAgICAiICAtIFJ1biBnYXJiYWdlIGNvbGxlY3Rpb246IGdjKClcblxuIiwNCiAgICAi4oCiIFBsb3R0aW5nIHNwZWVkOlxuIiwNCiAgICAiICAtIEJhc2UgUjogZmFzdGVzdCBmb3Igc2ltcGxlIHBsb3RzXG4iLA0KICAgICIgIC0gZ2dwbG90Mjogc2xvd2VyLCBtb3JlIGZlYXR1cmVzXG4iLA0KICAgICIgIC0gUGxvdGx5OiBzbG93ZXN0LCBidXQgaW50ZXJhY3RpdmVcbiINCikNCg0KYGBgDQoNCi0tLQ0KDQojIEZJTkFMIFBST0pFQ1QNCg0KYGBge3IgZmluYWwtcHJvamVjdC1pbnN0cnVjdGlvbnMsIGV2YWw9RkFMU0V9DQojIEZJTkFMIFBST0pFQ1Q6IENyZWF0ZSBhIFZpc3VhbGl6YXRpb24gRGFzaGJvYXJkDQojIA0KIyBDaG9vc2UgYW55IGRhdGFzZXQgKHN1Z2dlc3Rpb25zOiBnYXBtaW5kZXIsIGRpYW1vbmRzLCBlY29ub21pY3MpDQojIA0KIyBSZXF1aXJlbWVudHM6DQojIDEuIENyZWF0ZSBhdCBsZWFzdCAzIGRpZmZlcmVudCBwbG90IHR5cGVzDQojIDIuIFVzZSBhbGwgdGhyZWUgcGxvdHRpbmcgc3lzdGVtcyAoQmFzZSBSLCBnZ3Bsb3QyLCBQbG90bHkpDQojIDMuIEluY2x1ZGU6DQojICAgIC0gUHJvcGVyIGxhYmVscyBhbmQgdGl0bGVzDQojICAgIC0gTGVnZW5kcyB3aGVyZSBhcHByb3ByaWF0ZQ0KIyAgICAtIENvbG9yIHNjaGVtZXMNCiMgICAgLSBUaGVtZSBjdXN0b21pemF0aW9uDQojIA0KIyA0LiBFeHBvcnQgYWxsIHBsb3RzDQojIDUuIFdyaXRlIGJyaWVmIGFuYWx5c2lzIG9mIGZpbmRpbmdzDQojIA0KIyBFeGFtcGxlIHdvcmtmbG93Og0KIyAxLiBMb2FkIGFuZCBleHBsb3JlIGRhdGENCiMgMi4gQ3JlYXRlIEJhc2UgUiBwbG90cyBmb3IgcXVpY2sgZXhwbG9yYXRpb24NCiMgMy4gQ3JlYXRlIGdncGxvdDIgcGxvdHMgZm9yIHB1YmxpY2F0aW9uDQojIDQuIENyZWF0ZSBQbG90bHkgcGxvdHMgZm9yIGludGVyYWN0aXZpdHkNCiMgNS4gQ29tcGFyZSBpbnNpZ2h0cyBmcm9tIGRpZmZlcmVudCB2aXN1YWxpemF0aW9ucw0KYGBgDQoNCi0tLQ0KDQojIFNVTU1BUlkNCg0KIyMgS2V5IFRha2Vhd2F5cw0KDQoxLiAqKkJhc2UgUioqIGlzIGJlc3QgZm9yIHF1aWNrIGV4cGxvcmF0b3J5IGFuYWx5c2lzIGFuZCBzaW1wbGUgcGxvdHMNCjIuICoqZ2dwbG90MioqIGV4Y2VscyBhdCBjcmVhdGluZyBwdWJsaWNhdGlvbi1xdWFsaXR5LCBjb21wbGV4IHZpc3VhbGl6YXRpb25zDQozLiAqKlBsb3RseSoqIGlzIGlkZWFsIGZvciBpbnRlcmFjdGl2ZSwgd2ViLWJhc2VkIGRhc2hib2FyZHMNCjQuIEFsd2F5cyBjb25zaWRlciB5b3VyIGF1ZGllbmNlIGFuZCBwdXJwb3NlIHdoZW4gY2hvb3NpbmcgYSBwbG90dGluZyBtZXRob2QNCjUuIEdvb2QgdmlzdWFsaXphdGlvbnMgdGVsbCBhIHN0b3J5IC0gdXNlIHRpdGxlcywgbGFiZWxzLCBhbmQgYW5ub3RhdGlvbnMgZWZmZWN0aXZlbHkNCg0KDQojIyBBZGRpdGlvbmFsIFJlc291cmNlcw0KDQotICoqUiBHcmFwaCBHYWxsZXJ5Kio6IGh0dHBzOi8vci1ncmFwaC1nYWxsZXJ5LmNvbS8NCi0gKipnZ3Bsb3QyIERvY3VtZW50YXRpb24qKjogaHR0cHM6Ly9nZ3Bsb3QyLnRpZHl2ZXJzZS5vcmcvDQotICoqUGxvdGx5IFIgRG9jdW1lbnRhdGlvbioqOiBodHRwczovL3Bsb3RseS5jb20vci8NCi0gKipDb2xvckJyZXdlcioqOiBodHRwczovL2NvbG9yYnJld2VyMi5vcmcvDQotICoqRGF0YSBWaXN1YWxpemF0aW9uIENoZWNrbGlzdCoqOiBodHRwczovL3N0ZXBoYW5pZWV2ZXJncmVlbi5jb20vYXYtY2hlY2tsaXN0Lw0KDQotLS0NCioqVGhpcyBtYXRlcmlhbCBpcyBwYXJ0IG9mIHRoZSB0cmFpbmluZyBwcm9ncmFtIGJ5IFRoZSBOYXRpb25hbCBDZW50cmUgZm9yIFJlc2VhcmNoIE1ldGhvZHMgwqkgW05DUk1dKGh0dHBzOi8vd3d3Lm5jcm0uYWMudWsvYWJvdXQvKSBhdXRob3JlZCBieSBbRHLigK9Tb21uYXRo4oCvQ2hhdWRodXJpXShodHRwczovL3d3dy5zb3V0aGFtcHRvbi5hYy51ay9wZW9wbGUvNjVjdHE4L2RvY3Rvci1zb21uYXRoLWNoYXVkaHVyaSkgKFVuaXZlcnNpdHkgb2YgU291dGhhbXB0b24pLiBDb250ZW50IGlzIHVuZGVyIGEgQ0PigK9CWeKAkXN0eWxlIHBlcm1pc3NpdmUgbGljZW5zZSBhbmQgY2FuIGJlIGZyZWVseSB1c2VkIGZvciBlZHVjYXRpb25hbCBwdXJwb3NlcyB3aXRoIHByb3BlciBhdHRyaWJ1dGlvbi4qKg0K